diff --git a/Cargo.lock b/Cargo.lock index 3ac7f69..e88454e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,14 +45,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] -name = "async-trait" -version = "0.1.89" +name = "arboard" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "x11rb", ] [[package]] @@ -145,20 +154,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] -name = "byteorder" -version = "1.5.0" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" @@ -259,8 +258,8 @@ dependencies = [ "cxx-qt-build", "cxx-qt-lib", "dirs", + "edtui", "log", - "nvim-rs", "ratatui", "structopt", "syntect", @@ -269,6 +268,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -367,6 +375,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -633,6 +647,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.10.0", + "objc2", +] + [[package]] name = "document-features" version = "0.2.12" @@ -642,12 +666,47 @@ dependencies = [ "litrs", ] +[[package]] +name = "edtui" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417b85aa75bedb1da51eeed2d7a9241a061ddc6a0212e80057968ba34256fad8" +dependencies = [ + "arboard", + "crossterm", + "edtui-jagged", + "enum_dispatch", + "once_cell", + "ratatui-core", + "ratatui-widgets", + "syntect", + "unicode-width 0.2.2", +] + +[[package]] +name = "edtui-jagged" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6818b2d6b8b3da52f7491b6331e27d45ae34e5baaffeb1edfde43911fe63dd6" + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -664,6 +723,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "euclid" version = "0.22.11" @@ -683,6 +748,35 @@ dependencies = [ "regex", ] +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "filedescriptor" version = "0.8.3" @@ -734,103 +828,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures 0.1.31", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", - "tokio-io", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -841,6 +838,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -864,6 +871,17 @@ dependencies = [ "wasip2", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -911,6 +929,20 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png", + "tiff", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -943,15 +975,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "itertools" version = "0.13.0" @@ -1154,6 +1177,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "nix" version = "0.29.0" @@ -1213,18 +1246,76 @@ dependencies = [ ] [[package]] -name = "nvim-rs" -version = "0.9.2" +name = "objc2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010294fd782a554d4b9b17608305f7498809e857a6ed7a3e858588ad8e5691f5" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ - "async-trait", - "futures 0.3.31", - "log", - "rmp", - "rmpv", - "tokio", - "tokio-util", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", ] [[package]] @@ -1293,6 +1384,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pest" version = "2.8.5" @@ -1394,12 +1491,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" version = "0.3.32" @@ -1419,6 +1510,19 @@ dependencies = [ "time", ] +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.10.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "portable-atomic" version = "1.13.0" @@ -1464,6 +1568,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + [[package]] name = "qt-build-utils" version = "0.7.3" @@ -1475,6 +1588,12 @@ dependencies = [ "versions", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.38.4" @@ -1648,24 +1767,6 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" -[[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmpv" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417" -dependencies = [ - "rmp", -] - [[package]] name = "rustc_version" version = "0.4.1" @@ -1830,28 +1931,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -2079,6 +2164,20 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "tiff" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.45" @@ -2118,28 +2217,15 @@ version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "bytes 1.11.0", + "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", "tokio-macros", "windows-sys 0.61.2", ] -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log", -] - [[package]] name = "tokio-macros" version = "2.6.0" @@ -2151,20 +2237,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes 1.11.0", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "tui-tree-widget" version = "0.24.0" @@ -2342,6 +2414,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -2540,6 +2618,23 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -2549,8 +2644,43 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "zmij" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index f50d4c7..e4801f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ ratatui = "0.30.0" anyhow = "1.0.100" tui-tree-widget = "0.24.0" uuid = { version = "1.19.0", features = ["v4"] } -nvim-rs = { version = "0.9.2", features = ["use_tokio"] } tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros", "process"] } +edtui = "0.11.0" [build-dependencies] # The link_qt_object_files feature is required for statically linking Qt 6. diff --git a/src/main.rs b/src/main.rs index b3be5c8..cc84b45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ fn main() -> Result<(), Box> { true => gui::run(root_path), false => match args.tui { // Open the TUI editor if requested, otherwise use the QML GUI by default. - true => Ok(tui::start(root_path)?), + true => Ok(tui::Tui::new(root_path).start()?), false => { // Relaunch the CLIDE GUI in a separate process. Command::new(std::env::current_exe()?) diff --git a/src/tui.rs b/src/tui.rs index cfafaf9..ecb2ce7 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,16 +1,58 @@ pub mod app; mod component; -mod explorer; mod editor; +mod explorer; use anyhow::{Context, Result}; +use ratatui::Terminal; +use ratatui::backend::CrosstermBackend; +use ratatui::crossterm::event::{ + DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, +}; +use ratatui::crossterm::terminal::{ + EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode, +}; +use std::io::{Stdout, stdout}; -pub fn start(root_path: std::path::PathBuf) -> Result<()> { - println!("Starting the TUI editor at {:?}", root_path); - let terminal = ratatui::init(); - let app_result = app::App::new(&root_path) - .run(terminal) - .context("Failed to start the TUI editor."); - ratatui::restore(); - app_result +pub struct Tui { + terminal: Terminal>, + root_path: std::path::PathBuf, +} + +impl Tui { + pub fn new(root_path: std::path::PathBuf) -> Self { + Self { + terminal: Terminal::new(CrosstermBackend::new(stdout())) + .expect("Failed to initialize terminal"), + root_path, + } + } + + pub fn start(self) -> Result<()> { + println!("Starting the TUI editor at {:?}", self.root_path); + ratatui::crossterm::execute!( + stdout(), + EnterAlternateScreen, + EnableMouseCapture, + EnableBracketedPaste + )?; + enable_raw_mode()?; + + let app_result = app::App::new(&self.root_path) + .run(self.terminal) + .context("Failed to start the TUI editor."); + Self::stop()?; + app_result + } + + fn stop() -> Result<()> { + disable_raw_mode()?; + ratatui::crossterm::execute!( + stdout(), + LeaveAlternateScreen, + DisableMouseCapture, + DisableBracketedPaste + )?; + Ok(()) + } } diff --git a/src/tui/app.rs b/src/tui/app.rs index fa761b1..08d74c7 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1,34 +1,63 @@ use crate::tui::component::{Action, ClideComponent}; +use crate::tui::editor::Editor; use crate::tui::explorer::Explorer; use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; +use ratatui::crossterm::event; +use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; +use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::prelude::{Color, Style, Widget}; use ratatui::widgets::{Block, Borders, Padding, Paragraph, Tabs, Wrap}; use ratatui::{DefaultTerminal, symbols}; +use std::time::Duration; -#[derive(Debug, Clone)] pub struct App<'a> { explorer: Explorer<'a>, + editor: Editor, } impl<'a> App<'a> { pub(crate) fn new(root_path: &'a std::path::Path) -> Self { Self { explorer: Explorer::new(root_path), + editor: Editor::new(), } } + fn get_event(&mut self) -> Option { + if !event::poll(Duration::from_millis(250)).expect("event poll failed") { + return None; + } + + event::read().ok() + } + pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<()> { loop { terminal.draw(|f| { - f.render_widget(&self, f.area()); + f.render_widget(&mut self, f.area()); })?; // TODO: Handle events based on which component is active. - // match self.explorer.handle_events() { ... } - match self.handle_events() { - Action::Quit => break, - _ => {} + if let Some(event) = self.get_event() { + self.editor + .event_handler + .on_event(event.clone(), &mut self.editor.state); + + match event { + Event::FocusGained => {} + Event::FocusLost => {} + Event::Key(key_event) => { + // Handle main application key events. + match self.handle_key_events(key_event) { + Action::Noop => {} + Action::Quit => break, + Action::Pass => {} + } + } + Event::Mouse(_) => {} + Event::Paste(_) => {} + Event::Resize(_, _) => {} + } } } Ok(()) @@ -55,25 +84,6 @@ impl<'a> App<'a> { .render(area, buf); } - fn draw_editor(&self, area: Rect, buf: &mut Buffer) { - // TODO: Title should be detected programming language name - // TODO: Content should be file contents - // TODO: Contents should use vim in rendered TTY - // TODO: Vimrc should be used - Paragraph::new("This is an example of the TUI interface (press 'q' to quit)") - .style(Style::default()) - .block( - Block::default() - .title("Rust") - .title_style(Style::default().fg(Color::Yellow)) - .title_alignment(Alignment::Right) - .borders(Borders::ALL) - .padding(Padding::new(0, 0, 0, 1)), - ) - .wrap(Wrap { trim: false }) - .render(area, buf); - } - fn draw_terminal(&self, area: Rect, buf: &mut Buffer) { // TODO: Title should be detected shell name // TODO: Contents should be shell output @@ -91,7 +101,7 @@ impl<'a> App<'a> { } // TODO: Separate complex components into their own widgets. -impl<'a> Widget for &App<'a> { +impl<'a> Widget for &mut App<'a> { fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized, @@ -127,8 +137,23 @@ impl<'a> Widget for &App<'a> { self.explorer.render(horizontal[0], buf); self.draw_tabs(editor_layout[0], buf); - self.draw_editor(editor_layout[1], buf); + self.editor.render(editor_layout[1], buf); } } -impl<'a> ClideComponent for App<'a> {} +impl<'a> ClideComponent for App<'a> { + fn handle_key_events(&mut self, key: KeyEvent) -> Action { + match key { + KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + state: _state, + } => Action::Quit, + key_event => { + // Pass the key event to each component that can handle it. + self.explorer.handle_key_events(key_event) + } + } + } +} diff --git a/src/tui/component.rs b/src/tui/component.rs index c2bc1b7..0906154 100644 --- a/src/tui/component.rs +++ b/src/tui/component.rs @@ -1,38 +1,23 @@ -use ratatui::crossterm::event; -use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, MouseEvent}; -use std::time::Duration; +use ratatui::crossterm::event::{KeyEvent, MouseEvent}; pub enum Action { Noop, Quit, + Pass, // Pass input to another component. } pub trait ClideComponent { - fn handle_events(&mut self) -> Action { - if !event::poll(Duration::from_millis(250)).expect("event poll failed") { - return Action::Noop; - } - - let key_event = event::read().expect("event read failed"); - match key_event { - Event::Key(key_event) => self.handle_key_events(key_event), - Event::Mouse(mouse_event) => self.handle_mouse_events(mouse_event), - _ => Action::Noop, - } - } - - fn handle_key_events(&mut self, key: KeyEvent) -> Action { - match key.code { - KeyCode::Char('q') => Action::Quit, - _ => Action::Noop, - } - } - - fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Action { + fn handle_key_events(&mut self, _key: KeyEvent) -> Action { Action::Noop } - fn update(&mut self, action: Action) -> Action { + #[allow(dead_code)] + fn handle_mouse_events(&mut self, _mouse: MouseEvent) -> Action { + Action::Noop + } + + #[allow(dead_code)] + fn update(&mut self, _action: Action) -> Action { Action::Noop } } diff --git a/src/tui/editor.rs b/src/tui/editor.rs index 66939d3..9657184 100644 --- a/src/tui/editor.rs +++ b/src/tui/editor.rs @@ -1,156 +1,59 @@ -use crate::tui::component::ClideComponent; -use anyhow::Result; -use nvim_rs::compat::tokio::Compat; -use nvim_rs::{Handler, Neovim, UiAttachOptions, Value}; +use crate::tui::component::{Action, ClideComponent}; +use edtui::{ + EditorEventHandler, EditorState, EditorTheme, EditorView, LineNumbers, SyntaxHighlighter, +}; use ratatui::buffer::Buffer; -use ratatui::layout::Rect; -use ratatui::text::Line; -use ratatui::widgets::{Paragraph, Widget}; -use std::process::Stdio; -use std::sync::{Arc, Mutex}; -use tokio::process::Command; +use ratatui::crossterm::event::KeyEvent; +use ratatui::layout::{Alignment, Rect}; +use ratatui::prelude::{Color, Style}; +use ratatui::widgets::{Block, Borders, Padding, Widget}; -struct Editor { - ui: Arc, - height: usize, - width: usize, +// TODO: Consider using editor-command https://docs.rs/editor-command/latest/editor_command/ +// TODO: Title should be detected programming language name +// TODO: Content should be file contents +// TODO: Vimrc should be used +pub struct Editor { + pub state: EditorState, + pub event_handler: EditorEventHandler, } impl Editor { - fn new(height: usize, width: usize) -> Self { - let editor = Editor { - ui: Arc::new(NvimUI::default()), - height, - width, - }; - editor - .ui - .grid - .lock() - .unwrap() - .resize(height, vec![' '; width]); - editor + pub fn new() -> Self { + Editor { + state: EditorState::default(), + event_handler: EditorEventHandler::default(), + } } } -impl<'a> Widget for &Editor { +impl Widget for &mut Editor { fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized, { - let grid = self.ui.grid.lock().unwrap(); - - let lines: Vec = grid - .iter() - .map(|row| Line::from(row.iter().collect::())) - .collect(); - - Paragraph::new(lines).render(area, buf); + // TODO: Use current file extension for syntax highlighting here. + EditorView::new(&mut self.state) + .wrap(true) + .theme( + EditorTheme::default().block( + Block::default() + .title("Rust") + .title_style(Style::default().fg(Color::Yellow)) + .title_alignment(Alignment::Right) + .borders(Borders::ALL) + .padding(Padding::new(0, 0, 0, 1)), + ), + ) + .syntax_highlighter(SyntaxHighlighter::new("dracula", "rs").ok()) + .tab_width(2) + .line_numbers(LineNumbers::Absolute) + .render(area, buf); } } -impl ClideComponent for Editor {} - -#[derive(Default, Clone)] -pub struct NvimUI { - pub grid: Arc>>>, - pub cursor: Arc>, -} - -impl Handler for NvimUI { - type Writer = Compat; - - async fn handle_notify( - &self, - _name: String, - _args: Vec, - _neovim: Neovim>, - ) -> Result<()> { - if _name != "redraw" { - return Ok(()); - } - - for event in _args { - if let Value::Array(items) = event { - if items.is_empty() { - continue; - } - - let event_name = items[0].as_str().unwrap_or(""); - - match event_name { - "grid_line" => self.handle_grid_line(&items), - "cursor_goto" => self.handle_cursor(&items), - _ => {} - } - } - } - Ok(()) - } -} - -impl NvimUI { - fn handle_grid_line(&self, items: &[Value]) { - // ["grid_line", grid, row, col, cells] - let row = items[2].as_u64().unwrap() as usize; - let col = items[3].as_u64().unwrap() as usize; - - let cells = items[4].as_array().unwrap(); - - let mut grid = self.grid.lock().unwrap(); - let mut c = col; - - for cell in cells { - let cell_arr = cell.as_array().unwrap(); - let text = cell_arr[0].as_str().unwrap(); - let repeat = cell_arr.get(2).and_then(|v| v.as_u64()).unwrap_or(1); - - for _ in 0..repeat { - if let Some(ch) = text.chars().next() { - grid[row][c] = ch; - } - c += 1; - } - } - } - - fn handle_cursor(&self, items: &[Value]) { - let row = items[2].as_u64().unwrap() as usize; - let col = items[3].as_u64().unwrap() as usize; - *self.cursor.lock().unwrap() = (row, col); - } - - pub async fn spawn_nvim( - handler: H, - ) -> Result>> { - let mut child = Command::new("nvim") - .arg("--embed") - .arg("--headless") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - - let stdin = child.stdin.take().unwrap(); - let mut stdout = child.stdout.take().unwrap(); - - let (nvim, io_handler) = Neovim::new(stdout, stdin, handler); - - tokio::spawn(async move { - nvim_rs::compat::tokio::spawn(stdout.compat(), io_handler).await; - }); - - Ok(nvim) - } - - pub async fn init_ui( - nvim: &Neovim>, - w: i64, - h: i64, - ) -> anyhow::Result<()> { - let mut opts = UiAttachOptions::default(); - opts.set_rgb(true).set_linegrid_external(true); - - nvim.ui_attach(w, h, &opts).await?; - Ok(()) +impl ClideComponent for Editor { + fn handle_key_events(&mut self, key: KeyEvent) -> Action { + self.event_handler.on_key_event(key, &mut self.state); + Action::Pass } } diff --git a/src/tui/explorer.rs b/src/tui/explorer.rs index 63f7be0..37f2bee 100644 --- a/src/tui/explorer.rs +++ b/src/tui/explorer.rs @@ -17,15 +17,13 @@ pub struct Explorer<'a> { impl<'a> Explorer<'a> { pub fn new(path: &'a std::path::Path) -> Self { - let mut explorer = Explorer { + let explorer = Explorer { root_path: path, tree_items: Self::build_tree_from_path(path.into()), }; explorer } - pub fn draw(&self, area: Rect, buf: &mut Buffer) {} - fn build_tree_from_path(path: std::path::PathBuf) -> TreeItem<'static, String> { let mut children = vec![]; if let Ok(entries) = fs::read_dir(&path) {