From 3d2a6d6a5aa47cace356fe39184f2ac018898552 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 15 Nov 2022 12:21:36 +0100 Subject: [PATCH] refactor(plugins): change the data flow (#1934) * refactor(plugins): do not block render loop * refactor(plugins): cleanup * style(fmt): rustfmt * fix(plugins): various rendering pipeline fixes --- Cargo.lock | 681 ++++++++++++++---- default-plugins/compact-bar/src/main.rs | 4 +- default-plugins/status-bar/src/main.rs | 2 +- default-plugins/strider/src/main.rs | 15 +- default-plugins/tab-bar/src/main.rs | 4 +- src/install.rs | 1 - src/tests/e2e/cases.rs | 5 +- zellij-server/Cargo.toml | 4 +- zellij-server/src/logging_pipe.rs | 1 - zellij-server/src/panes/floating_panes/mod.rs | 38 +- zellij-server/src/panes/grid.rs | 79 +- zellij-server/src/panes/plugin_pane.rs | 308 ++++---- zellij-server/src/panes/terminal_pane.rs | 68 +- zellij-server/src/panes/tiled_panes/mod.rs | 62 +- zellij-server/src/pty.rs | 2 +- zellij-server/src/pty_writer.rs | 2 +- zellij-server/src/screen.rs | 14 +- zellij-server/src/tab/mod.rs | 84 ++- zellij-server/src/thread_bus.rs | 2 +- zellij-server/src/wasm_vm.rs | 126 ++-- zellij-utils/src/errors.rs | 2 + zellij-utils/src/pane_size.rs | 9 + 22 files changed, 1055 insertions(+), 458 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53bdb62b..680a3b0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli 0.26.1", + "gimli", ] [[package]] @@ -17,6 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -226,25 +237,22 @@ dependencies = [ "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.28.4", + "object", "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -313,6 +321,27 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -358,7 +387,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.44", "winapi", ] @@ -472,12 +501,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "corosensei" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "scopeguard", + "windows-sys 0.33.0", +] + [[package]] name = "cpufeatures" version = "0.2.2" @@ -489,62 +537,56 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9221545c0507dc08a62b2d8b5ffe8e17ac580b0a74d1813b496b8d70b070fbd0" +checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9936ea608b6cd176f107037f6adbb4deac933466fc7231154f96598b2d3ab1" +checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" dependencies = [ - "byteorder", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli 0.22.0", + "gimli", "log", "regalloc", "smallvec", "target-lexicon", - "thiserror", ] [[package]] name = "cranelift-codegen-meta" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef2b2768568306540f4c8db3acce9105534d34c4a1e440529c1e702d7f8c8d7" +checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" dependencies = [ "cranelift-codegen-shared", - "cranelift-entity", ] [[package]] name = "cranelift-codegen-shared" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6759012d6d19c4caec95793f052613e9d4113e925e7f14154defbac0f1d4c938" +checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" [[package]] name = "cranelift-entity" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86badbce14e15f52a45b666b38abe47b204969dd7f8fb7488cb55dd46b361fa6" -dependencies = [ - "serde", -] +checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" [[package]] name = "cranelift-frontend" -version = "0.68.0" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b608bb7656c554d0a4cf8f50c7a10b857e80306f6ff829ad6d468a7e2323c8d8" +checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" dependencies = [ "cranelift-codegen", "log", @@ -788,6 +830,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "either" version = "1.6.1" @@ -800,6 +848,26 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumset" version = "1.0.11" @@ -1007,7 +1075,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" dependencies = [ "cfg-if 0.1.10", - "serde", ] [[package]] @@ -1073,21 +1140,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.22.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" dependencies = [ "fallible-iterator", "indexmap", "stable_deref_trait", ] -[[package]] -name = "gimli" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" - [[package]] name = "gloo-timers" version = "0.2.4" @@ -1105,6 +1166,18 @@ name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -1172,7 +1245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", "serde", ] @@ -1305,9 +1378,9 @@ checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libloading" -version = "0.6.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -1398,6 +1471,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "loupe" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" +dependencies = [ + "indexmap", + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "mach" version = "0.3.2" @@ -1427,9 +1521,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.2.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] @@ -1648,22 +1742,15 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" -dependencies = [ - "crc32fast", - "indexmap", -] - [[package]] name = "object" version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ + "crc32fast", + "hashbrown 0.11.2", + "indexmap", "memchr", ] @@ -1779,7 +1866,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -2006,6 +2093,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.39" @@ -2015,6 +2108,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.18" @@ -2151,9 +2264,9 @@ dependencies = [ [[package]] name = "regalloc" -version = "0.0.31" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" dependencies = [ "log", "rustc-hash", @@ -2179,9 +2292,9 @@ checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "region" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ "bitflags", "libc", @@ -2198,6 +2311,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rmp" version = "0.8.11" @@ -2232,6 +2379,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "ryu" version = "1.0.10" @@ -2244,15 +2406,36 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.2", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.2" @@ -2336,6 +2519,21 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -2464,6 +2662,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + [[package]] name = "status-bar" version = "0.1.0" @@ -2480,6 +2687,55 @@ dependencies = [ "zellij-tile-utils", ] +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "strider" version = "0.2.0" @@ -2599,9 +2855,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.11.2" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" [[package]] name = "tempfile" @@ -2683,7 +2939,7 @@ dependencies = [ "pest_derive", "phf 0.10.1", "regex", - "semver", + "semver 0.11.0", "sha2", "signal-hook 0.1.17", "siphasher", @@ -2758,6 +3014,44 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -3094,21 +3388,25 @@ dependencies = [ [[package]] name = "wasmer" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a70cfae554988d904d64ca17ab0e7cd652ee5c8a0807094819c1ea93eb9d6866" +checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "indexmap", + "js-sys", + "loupe", "more-asserts", "target-lexicon", "thiserror", + "wasm-bindgen", + "wasmer-artifact", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-derive", "wasmer-engine", - "wasmer-engine-jit", - "wasmer-engine-native", + "wasmer-engine-dylib", + "wasmer-engine-universal", "wasmer-types", "wasmer-vm", "wat", @@ -3116,46 +3414,61 @@ dependencies = [ ] [[package]] -name = "wasmer-compiler" -version = "1.0.2" +name = "wasmer-artifact" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7732a9cab472bd921d5a0c422f45b3d03f62fa2c40a89e0770cef6d47e383e" +checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" dependencies = [ "enumset", + "loupe", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" +dependencies = [ + "enumset", + "loupe", + "rkyv", "serde", "serde_bytes", "smallvec", "target-lexicon", "thiserror", "wasmer-types", - "wasmer-vm", "wasmparser", ] [[package]] name = "wasmer-compiler-cranelift" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb9395f094e1d81534f4c5e330ed4cdb424e8df870d29ad585620284f5fddb" +checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" dependencies = [ "cranelift-codegen", + "cranelift-entity", "cranelift-frontend", - "gimli 0.22.0", + "gimli", + "loupe", "more-asserts", "rayon", - "serde", "smallvec", + "target-lexicon", "tracing", "wasmer-compiler", "wasmer-types", - "wasmer-vm", ] [[package]] name = "wasmer-derive" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b86dcd2c3efdb8390728a2b56f762db07789aaa5aa872a9dc776ba3a7912ed" +checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" dependencies = [ "proc-macro-error", "proc-macro2", @@ -3165,13 +3478,14 @@ dependencies = [ [[package]] name = "wasmer-engine" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe4667d6bd888f26ae8062a63a9379fa697415b4b4e380f33832e8418fd71b5" +checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" dependencies = [ "backtrace", - "bincode", + "enumset", "lazy_static", + "loupe", "memmap2", "more-asserts", "rustc-demangle", @@ -3179,42 +3493,30 @@ dependencies = [ "serde_bytes", "target-lexicon", "thiserror", + "wasmer-artifact", "wasmer-compiler", "wasmer-types", "wasmer-vm", ] [[package]] -name = "wasmer-engine-jit" -version = "1.0.2" +name = "wasmer-engine-dylib" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26770be802888011b4a3072f2a282fc2faa68aa48c71b3db6252a3937a85f3da" +checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" dependencies = [ - "bincode", - "cfg-if 0.1.10", - "region", - "serde", - "serde_bytes", - "wasmer-compiler", - "wasmer-engine", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-engine-native" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb4083a6c69f2cd4b000b82a80717f37c6cc2e536aee3a8ffe9af3edc276a8b" -dependencies = [ - "bincode", - "cfg-if 0.1.10", + "cfg-if 1.0.0", + "enum-iterator", + "enumset", "leb128", "libloading", + "loupe", + "object", + "rkyv", "serde", "tempfile", "tracing", + "wasmer-artifact", "wasmer-compiler", "wasmer-engine", "wasmer-object", @@ -3224,12 +3526,48 @@ dependencies = [ ] [[package]] -name = "wasmer-object" -version = "1.0.2" +name = "wasmer-engine-universal" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf8e0c12b82ff81ebecd30d7e118be5fec871d6de885a90eeb105df0a769a7b" +checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" dependencies = [ - "object 0.22.0", + "cfg-if 1.0.0", + "enumset", + "leb128", + "loupe", + "region", + "rkyv", + "wasmer-compiler", + "wasmer-engine", + "wasmer-engine-universal-artifact", + "wasmer-types", + "wasmer-vm", + "winapi", +] + +[[package]] +name = "wasmer-engine-universal-artifact" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" +dependencies = [ + "enum-iterator", + "enumset", + "loupe", + "rkyv", + "thiserror", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-object" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" +dependencies = [ + "object", "thiserror", "wasmer-compiler", "wasmer-types", @@ -3237,60 +3575,94 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f4ac28c2951cd792c18332f03da523ed06b170f5cf6bb5b1bdd7e36c2a8218" +checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" dependencies = [ - "cranelift-entity", + "backtrace", + "enum-iterator", + "indexmap", + "loupe", + "more-asserts", + "rkyv", "serde", "thiserror", ] [[package]] -name = "wasmer-vm" -version = "1.0.2" +name = "wasmer-vfs" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7635ba0b6d2fd325f588d69a950ad9fa04dddbf6ad08b6b2a183146319bf6ae" +checksum = "9302eae3edc53cb540c2d681e7f16d8274918c1ce207591f04fed351649e97c0" +dependencies = [ + "libc", + "thiserror", + "tracing", +] + +[[package]] +name = "wasmer-vm" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" dependencies = [ "backtrace", "cc", - "cfg-if 0.1.10", + "cfg-if 1.0.0", + "corosensei", + "enum-iterator", "indexmap", + "lazy_static", "libc", + "loupe", + "mach", "memoffset", "more-asserts", "region", + "rkyv", + "scopeguard", "serde", "thiserror", + "wasmer-artifact", "wasmer-types", "winapi", ] [[package]] name = "wasmer-wasi" -version = "1.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf3ec2503b6b12034cf066deb923805952f821223b881acb746df83e284c03e" +checksum = "fadbe31e3c1b6f3e398ad172b169152ae1a743ae6efd5f9ffb34019983319d99" dependencies = [ - "bincode", - "byteorder", + "cfg-if 1.0.0", "generational-arena", "getrandom 0.2.7", "libc", - "serde", "thiserror", - "time", "tracing", - "typetag", + "wasm-bindgen", "wasmer", + "wasmer-vfs", + "wasmer-wasi-types", "winapi", ] [[package]] -name = "wasmparser" -version = "0.65.0" +name = "wasmer-wasi-types" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" +checksum = "22dc83aadbdf97388de3211cb6f105374f245a3cf2a5c65a16776e7a087a8468" +dependencies = [ + "byteorder", + "time 0.2.27", + "wasmer-types", +] + +[[package]] +name = "wasmparser" +version = "0.83.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wast" @@ -3420,43 +3792,86 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -3520,7 +3935,7 @@ dependencies = [ "highway", "insta", "log", - "semver", + "semver 0.11.0", "serde_json", "sixel-image", "sixel-tokenizer", diff --git a/default-plugins/compact-bar/src/main.rs b/default-plugins/compact-bar/src/main.rs index 2d52a6f9..5dc8f2ff 100644 --- a/default-plugins/compact-bar/src/main.rs +++ b/default-plugins/compact-bar/src/main.rs @@ -129,10 +129,10 @@ impl ZellijPlugin for State { }; match background { PaletteColor::Rgb((r, g, b)) => { - println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b); + print!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b); }, PaletteColor::EightBit(color) => { - println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color); + print!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color); }, } self.should_render = false; diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 800fb82c..25e31d2f 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -232,7 +232,7 @@ impl ZellijPlugin for State { } if rows > 1 { - println!("\u{1b}[m{}\u{1b}[0K", second_line); + print!("\u{1b}[m{}\u{1b}[0K", second_line); } } } diff --git a/default-plugins/strider/src/main.rs b/default-plugins/strider/src/main.rs index 22ca1c61..fcac6ec6 100644 --- a/default-plugins/strider/src/main.rs +++ b/default-plugins/strider/src/main.rs @@ -93,6 +93,7 @@ impl ZellijPlugin for State { *self.scroll_mut() = self.selected() + 2 - rows; } + let is_last_row = i == rows.saturating_sub(1); let i = self.scroll() + i; if let Some(entry) = self.files.get(i) { let mut path = entry.as_line(cols).normal(); @@ -102,11 +103,19 @@ impl ZellijPlugin for State { } if i == self.selected() { - println!("{}", path.reversed()); + if is_last_row { + print!("{}", path.reversed()); + } else { + println!("{}", path.reversed()); + } } else { - println!("{}", path); + if is_last_row { + print!("{}", path); + } else { + println!("{}", path); + } } - } else { + } else if !is_last_row { println!(); } } diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs index 562e2779..f0310261 100644 --- a/default-plugins/tab-bar/src/main.rs +++ b/default-plugins/tab-bar/src/main.rs @@ -127,10 +127,10 @@ impl ZellijPlugin for State { }; match background { PaletteColor::Rgb((r, g, b)) => { - println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b); + print!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b); }, PaletteColor::EightBit(color) => { - println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color); + print!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color); }, } self.should_render = false; diff --git a/src/install.rs b/src/install.rs index 1f50a1d6..53c7122a 100644 --- a/src/install.rs +++ b/src/install.rs @@ -19,7 +19,6 @@ macro_rules! asset_map { #[cfg(not(feature = "disable_automatic_asset_installation"))] pub(crate) fn populate_data_dir(data_dir: &Path) { - // First run installation of default plugins & layouts let mut assets = asset_map! { "assets/plugins/compact-bar.wasm" => "plugins/compact-bar.wasm", "assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm", diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 359d7416..418b08f9 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -847,7 +847,10 @@ pub fn resize_terminal_window() { name: "wait for terminal to be resized and app to be re-rendered", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(53, 2) && remote_terminal.tip_appears() { + if remote_terminal.cursor_position_is(53, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Ctrl +") + { // size has been changed step_is_complete = true; } diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml index 22112db8..e8af7e5f 100644 --- a/zellij-server/Cargo.toml +++ b/zellij-server/Cargo.toml @@ -18,8 +18,8 @@ daemonize = "0.4.1" serde_json = "1.0" unicode-width = "0.1.8" url = "2.2.2" -wasmer = "1.0.0" -wasmer-wasi = "1.0.0" +wasmer = "2.3.0" +wasmer-wasi = "2.3.0" cassowary = "0.3.0" zellij-utils = { path = "../zellij-utils/", version = "0.34.0" } log = "0.4.17" diff --git a/zellij-server/src/logging_pipe.rs b/zellij-server/src/logging_pipe.rs index d54de9b0..6081b283 100644 --- a/zellij-server/src/logging_pipe.rs +++ b/zellij-server/src/logging_pipe.rs @@ -114,7 +114,6 @@ impl Seek for LoggingPipe { } } -#[typetag::serde] impl WasiFile for LoggingPipe { fn last_accessed(&self) -> u64 { 0 diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index c8a60681..5eb73f43 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -1,6 +1,7 @@ mod floating_pane_grid; use zellij_utils::position::Position; +use crate::resize_pty; use crate::tab::Pane; use floating_pane_grid::FloatingPaneGrid; @@ -8,7 +9,9 @@ use crate::{ os_input_output::ServerOsApi, output::{FloatingPanesStack, Output}, panes::{ActivePanes, PaneId}, + thread_bus::ThreadSenders, ui::pane_contents_and_ui::PaneContentsAndUi, + wasm_vm::PluginInstruction, ClientId, }; use std::cell::RefCell; @@ -22,22 +25,6 @@ use zellij_utils::{ pane_size::{Offset, PaneGeom, Size, Viewport}, }; -macro_rules! resize_pty { - ($pane:expr, $os_input:expr) => { - if let PaneId::Terminal(ref pid) = $pane.pid() { - // FIXME: This `set_terminal_size_using_terminal_id` call would be best in - // `TerminalPane::reflow_lines` - $os_input.set_terminal_size_using_terminal_id( - *pid, - $pane.get_content_columns() as u16, - $pane.get_content_rows() as u16, - ) - } else { - Ok(()) - } - }; -} - pub struct FloatingPanes { panes: BTreeMap>, display_area: Rc>, @@ -53,6 +40,7 @@ pub struct FloatingPanes { active_panes: ActivePanes, show_panes: bool, pane_being_moved_with_mouse: Option<(PaneId, Position)>, + senders: ThreadSenders, } #[allow(clippy::borrowed_box)] @@ -68,6 +56,7 @@ impl FloatingPanes { default_mode_info: ModeInfo, style: Style, os_input: Box, + senders: ThreadSenders, ) -> Self { FloatingPanes { panes: BTreeMap::new(), @@ -84,6 +73,7 @@ impl FloatingPanes { show_panes: false, active_panes: ActivePanes::new(&os_input), pane_being_moved_with_mouse: None, + senders, } } pub fn stack(&self) -> Option { @@ -241,7 +231,7 @@ impl FloatingPanes { } else { pane.set_content_offset(Offset::default()); } - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } } pub fn render(&mut self, output: &mut Output) -> Result<()> { @@ -321,7 +311,7 @@ impl FloatingPanes { } pub fn resize_pty_all_panes(&mut self, os_api: &mut Box) { for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } } pub fn resize_active_pane_left( @@ -341,7 +331,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_pane_left(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; @@ -365,7 +355,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_pane_right(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; @@ -389,7 +379,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_pane_down(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; @@ -413,7 +403,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_pane_up(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; @@ -437,7 +427,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_increase(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; @@ -461,7 +451,7 @@ impl FloatingPanes { ); floating_pane_grid.resize_decrease(active_floating_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, os_api).unwrap(); + resize_pty!(pane, os_api, self.senders).unwrap(); } self.set_force_render(); return true; diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs index f570a446..0bc2b767 100644 --- a/zellij-server/src/panes/grid.rs +++ b/zellij-server/src/panes/grid.rs @@ -4,6 +4,8 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use unicode_width::UnicodeWidthChar; +use zellij_utils::data::Style; +use zellij_utils::errors::prelude::*; use zellij_utils::regex::Regex; use std::{ @@ -549,7 +551,7 @@ impl Grid { pub fn cursor_shape(&self) -> CursorShape { self.cursor.get_shape() } - pub fn scrollback_position_and_length(&mut self) -> (usize, usize) { + pub fn scrollback_position_and_length(&self) -> (usize, usize) { // (position, length) ( self.lines_below.len(), @@ -979,6 +981,71 @@ impl Grid { (changed_character_chunks, changed_sixel_image_chunks) } + pub fn render( + &mut self, + content_x: usize, + content_y: usize, + style: &Style, + ) -> Result, Option, Vec)>> { + let mut raw_vte_output = String::new(); + + let (mut character_chunks, sixel_image_chunks) = self.read_changes(content_x, content_y); + for character_chunk in character_chunks.iter_mut() { + character_chunk.add_changed_colors(self.changed_colors); + if self + .selection + .contains_row(character_chunk.y.saturating_sub(content_y)) + { + let background_color = match style.colors.bg { + PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), + PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), + }; + character_chunk.add_selection_and_colors( + self.selection, + background_color, + None, + content_x, + content_y, + ); + } else if !self.search_results.selections.is_empty() { + for res in self.search_results.selections.iter() { + if res.contains_row(character_chunk.y.saturating_sub(content_y)) { + let (select_background_palette, select_foreground_palette) = + if Some(res) == self.search_results.active.as_ref() { + (style.colors.orange, style.colors.black) + } else { + (style.colors.green, style.colors.black) + }; + let background_color = match select_background_palette { + PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), + PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), + }; + let foreground_color = match select_foreground_palette { + PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), + PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), + }; + character_chunk.add_selection_and_colors( + *res, + background_color, + Some(foreground_color), + content_x, + content_y, + ); + } + } + } + } + if self.ring_bell { + let ring_bell = '\u{7}'; + raw_vte_output.push(ring_bell); + self.ring_bell = false; + } + return Ok(Some(( + character_chunks, + Some(raw_vte_output), + sixel_image_chunks, + ))); + } pub fn cursor_coordinates(&self) -> Option<(usize, usize)> { if self.cursor_is_hidden { None @@ -1959,6 +2026,14 @@ impl Grid { None } } + pub fn delete_viewport_and_scroll(&mut self) { + self.lines_above.clear(); + self.viewport.clear(); + self.lines_below.clear(); + } + pub fn reset_cursor_position(&mut self) { + self.cursor = Cursor::new(0, 0); + } } impl Perform for Grid { @@ -2563,7 +2638,7 @@ impl Perform for Grid { }; if first_intermediate_is_questionmark { let query_type = params_iter.next(); - let is_query = params_iter.next() == Some(&[1]); + let is_query = matches!(params_iter.next(), Some(&[1])); if is_query { // XTSMGRAPHICS match query_type { diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index 81610188..9353b511 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -1,28 +1,50 @@ -use std::fmt::Write; -use std::sync::mpsc::channel; +use std::collections::HashMap; use std::time::Instant; use crate::output::{CharacterChunk, SixelImageChunk}; -use crate::panes::PaneId; +use crate::panes::{grid::Grid, sixel::SixelImageStore, LinkHandler, PaneId}; use crate::pty::VteBytes; use crate::tab::Pane; use crate::ui::pane_boundaries_frame::{FrameParams, PaneFrame}; use crate::wasm_vm::PluginInstruction; use crate::ClientId; -use zellij_utils::pane_size::Offset; +use std::cell::RefCell; +use std::rc::Rc; +use zellij_utils::pane_size::{Offset, SizeInPixels}; use zellij_utils::position::Position; -use zellij_utils::shared::ansi_len; use zellij_utils::{ channels::SenderWithContext, - data::{Event, InputMode, Mouse, PaletteColor}, + data::{Event, InputMode, Mouse, Palette, PaletteColor, Style}, errors::prelude::*, pane_size::{Dimension, PaneGeom}, shared::make_terminal_title, + vte, }; +macro_rules! get_or_create_grid { + ($self:ident, $client_id:ident) => {{ + let rows = $self.get_content_rows(); + let cols = $self.get_content_columns(); + + $self.grids.entry($client_id).or_insert_with(|| { + let mut grid = Grid::new( + rows, + cols, + $self.terminal_emulator_colors.clone(), + $self.terminal_emulator_color_codes.clone(), + $self.link_handler.clone(), + $self.character_cell_size.clone(), + $self.sixel_image_store.clone(), + ); + grid.hide_cursor(); + grid + }) + }}; +} + pub(crate) struct PluginPane { pub pid: u32, - pub should_render: bool, + pub should_render: HashMap, pub selectable: bool, pub geom: PaneGeom, pub geom_override: Option, @@ -31,8 +53,16 @@ pub(crate) struct PluginPane { pub active_at: Instant, pub pane_title: String, pub pane_name: String, + pub style: Style, + sixel_image_store: Rc>, + terminal_emulator_colors: Rc>, + terminal_emulator_color_codes: Rc>>, + link_handler: Rc>, + character_cell_size: Rc>>, + vte_parsers: HashMap, + grids: HashMap, prev_pane_name: String, - frame: bool, + frame: HashMap, borderless: bool, } @@ -43,21 +73,35 @@ impl PluginPane { send_plugin_instructions: SenderWithContext, title: String, pane_name: String, + sixel_image_store: Rc>, + terminal_emulator_colors: Rc>, + terminal_emulator_color_codes: Rc>>, + link_handler: Rc>, + character_cell_size: Rc>>, + style: Style, ) -> Self { Self { pid, - should_render: true, + should_render: HashMap::new(), selectable: true, geom: position_and_size, geom_override: None, send_plugin_instructions, active_at: Instant::now(), - frame: false, + frame: HashMap::new(), content_offset: Offset::default(), pane_title: title, borderless: false, pane_name: pane_name.clone(), prev_pane_name: pane_name, + terminal_emulator_colors, + terminal_emulator_color_codes, + link_handler, + character_cell_size, + sixel_image_store, + vte_parsers: HashMap::new(), + grids: HashMap::new(), + style, } } } @@ -98,18 +142,37 @@ impl Pane for PluginPane { } fn reset_size_and_position_override(&mut self) { self.geom_override = None; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn set_geom(&mut self, position_and_size: PaneGeom) { self.geom = position_and_size; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn set_geom_override(&mut self, pane_geom: PaneGeom) { self.geom_override = Some(pane_geom); - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } - fn handle_pty_bytes(&mut self, _event: VteBytes) { - // noop + fn handle_plugin_bytes(&mut self, client_id: ClientId, bytes: VteBytes) { + self.set_client_should_render(client_id, true); + let grid = get_or_create_grid!(self, client_id); + + // this is part of the plugin contract, whenever we update the plugin and call its render function, we delete the existing viewport + // and scroll, reset the cursor position and make sure all the viewport is rendered + grid.delete_viewport_and_scroll(); + grid.reset_cursor_position(); + grid.render_full_viewport(); + + let vte_parser = self + .vte_parsers + .entry(client_id) + .or_insert_with(|| vte::Parser::new()); + for &byte in &bytes { + vte_parser.advance(grid, byte); + } + self.should_render.insert(client_id, true); } fn cursor_coordinates(&self) -> Option<(usize, usize)> { None @@ -124,10 +187,21 @@ impl Pane for PluginPane { self.geom_override } fn should_render(&self) -> bool { - self.should_render + // set should_render for all clients + self.should_render.values().any(|v| *v) } fn set_should_render(&mut self, should_render: bool) { - self.should_render = should_render; + self.should_render + .values_mut() + .for_each(|v| *v = should_render); + } + fn render_full_viewport(&mut self) { + // this marks the pane for a full re-render, rather than just rendering the + // diff as it usually does with the OutputBuffer + self.frame.clear(); + for grid in self.grids.values_mut() { + grid.render_full_viewport(); + } } fn selectable(&self) -> bool { self.selectable @@ -139,127 +213,86 @@ impl Pane for PluginPane { &mut self, client_id: Option, ) -> Result, Option, Vec)>> { - // this is a bit of a hack but works in a pinch - let client_id = match client_id { - Some(id) => id, - None => return Ok(None), - }; - // if self.should_render { - if true { - let err_context = || format!("failed to render plugin panes for client {client_id}"); - - // while checking should_render rather than rendering each pane every time - // is more performant, it causes some problems when the pane to the left should be - // rendered and has wide characters (eg. Chinese characters or emoji) - // as a (hopefully) temporary hack, we render all panes until we find a better solution - let mut vte_output = String::new(); - let (buf_tx, buf_rx) = channel(); - - self.send_plugin_instructions - .send(PluginInstruction::Render( - buf_tx, - self.pid, - client_id, - self.get_content_rows(), - self.get_content_columns(), - )) - .to_anyhow() - .with_context(err_context)?; - - self.should_render = false; - // This is where we receive the text to render from the plugins. - let contents = buf_rx - .recv() - .with_context(err_context) - .to_log() - .unwrap_or("No output from plugin received. See logs".to_string()); - for (index, line) in contents.lines().enumerate() { - let actual_len = ansi_len(line); - let line_to_print = if actual_len > self.get_content_columns() { - let mut line = String::from(line); - line.truncate(self.get_content_columns()); - line - } else { - [ - line, - &str::repeat(" ", self.get_content_columns() - ansi_len(line)), - ] - .concat() - }; - - write!( - &mut vte_output, - "\u{1b}[{};{}H\u{1b}[m{}", - self.get_content_y() + 1 + index, - self.get_content_x() + 1, - line_to_print, - ) - .with_context(err_context)?; // goto row/col and reset styles - let line_len = line_to_print.len(); - if line_len < self.get_content_columns() { - // pad line - for _ in line_len..self.get_content_columns() { - vte_output.push(' '); - } - } - } - let total_line_count = contents.lines().count(); - if total_line_count < self.get_content_rows() { - // pad lines - for line_index in total_line_count..self.get_content_rows() { - let x = self.get_content_x(); - let y = self.get_content_y(); - write!( - &mut vte_output, - "\u{1b}[{};{}H\u{1b}[m", - y + line_index + 1, - x + 1 - ) - .with_context(err_context)?; // goto row/col and reset styles - for _col_index in 0..self.get_content_columns() { - vte_output.push(' '); - } - } - } - Ok(Some((vec![], Some(vte_output), vec![]))) // TODO: PluginPanes should have their own grid so that we can return the non-serialized TerminalCharacters and have them participate in the render buffer - } else { - Ok(None) + if client_id.is_none() { + return Ok(None); } + if let Some(client_id) = client_id { + if self.should_render.get(&client_id).copied().unwrap_or(false) { + let content_x = self.get_content_x(); + let content_y = self.get_content_y(); + if let Some(grid) = self.grids.get_mut(&client_id) { + match grid.render(content_x, content_y, &self.style) { + Ok(rendered_assets) => { + self.should_render.insert(client_id, false); + return Ok(rendered_assets); + }, + e => return e, + } + } + } + } + Ok(None) } fn render_frame( &mut self, - _client_id: ClientId, + client_id: ClientId, frame_params: FrameParams, input_mode: InputMode, ) -> Result, Option)>> { - // FIXME: This is a hack that assumes all fixed-size panes are borderless. This - // will eventually need fixing! - let res = if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) { + if self.borderless { + return Ok(None); + } + if let Some(grid) = self.grids.get(&client_id) { + let err_context = || format!("failed to render frame for client {client_id}"); let pane_title = if self.pane_name.is_empty() && input_mode == InputMode::RenamePane && frame_params.is_main_client { String::from("Enter name...") } else if self.pane_name.is_empty() { - self.pane_title.clone() + grid.title + .clone() + .unwrap_or_else(|| self.pane_title.clone()) } else { self.pane_name.clone() }; + let frame = PaneFrame::new( self.current_geom().into(), - (0, 0), // scroll position + grid.scrollback_position_and_length(), pane_title, frame_params, ); - Some( - frame - .render() - .with_context(|| format!("failed to render frame for client {_client_id}"))?, - ) + + let res = match self.frame.get(&client_id) { + // TODO: use and_then or something? + Some(last_frame) => { + if &frame != last_frame { + if !self.borderless { + let frame_output = frame.render().with_context(err_context)?; + self.frame.insert(client_id, frame); + Some(frame_output) + } else { + None + } + } else { + None + } + }, + None => { + if !self.borderless { + let frame_output = frame.render().with_context(err_context)?; + self.frame.insert(client_id, frame); + Some(frame_output) + } else { + None + } + }, + }; + Ok(res) } else { - None - }; - Ok(res) + Ok(None) + } } fn render_fake_cursor( &mut self, @@ -298,42 +331,50 @@ impl Pane for PluginPane { fn reduce_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { self.geom.rows = Dimension::percent(p - percent); - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } } fn increase_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { self.geom.rows = Dimension::percent(p + percent); - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } } fn reduce_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { self.geom.cols = Dimension::percent(p - percent); - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } } fn increase_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { self.geom.cols = Dimension::percent(p + percent); - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } } fn push_down(&mut self, count: usize) { self.geom.y += count; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn push_right(&mut self, count: usize) { self.geom.x += count; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn pull_left(&mut self, count: usize) { self.geom.x -= count; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn pull_up(&mut self, count: usize) { self.geom.y -= count; - self.should_render = true; + self.resize_grids(); + self.set_should_render(true); } fn scroll_up(&mut self, count: usize, client_id: ClientId) { self.send_plugin_instructions @@ -394,11 +435,12 @@ impl Pane for PluginPane { fn set_active_at(&mut self, time: Instant) { self.active_at = time; } - fn set_frame(&mut self, frame: bool) { - self.frame = frame; + fn set_frame(&mut self, _frame: bool) { + self.frame.clear(); } fn set_content_offset(&mut self, offset: Offset) { self.content_offset = offset; + self.resize_grids(); } fn store_pane_name(&mut self) { @@ -428,3 +470,17 @@ impl Pane for PluginPane { .unwrap(); } } + +impl PluginPane { + fn resize_grids(&mut self) { + let content_rows = self.get_content_rows(); + let content_columns = self.get_content_columns(); + for grid in self.grids.values_mut() { + grid.change_size(content_rows, content_columns); + } + self.set_should_render(true); + } + fn set_client_should_render(&mut self, client_id: ClientId, should_render: bool) { + self.should_render.insert(client_id, should_render); + } +} diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 0cb3601a..b5c51c65 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -1,10 +1,10 @@ use crate::output::{CharacterChunk, SixelImageChunk}; use crate::panes::sixel::SixelImageStore; +use crate::panes::LinkHandler; use crate::panes::{ grid::Grid, terminal_character::{render_first_run_banner, TerminalCharacter, EMPTY_TERMINAL_CHARACTER}, }; -use crate::panes::{AnsiCode, LinkHandler}; use crate::pty::VteBytes; use crate::tab::{AdjustedInput, Pane}; use crate::ClientId; @@ -280,69 +280,15 @@ impl Pane for TerminalPane { _client_id: Option, ) -> Result, Option, Vec)>> { if self.should_render() { - let mut raw_vte_output = String::new(); let content_x = self.get_content_x(); let content_y = self.get_content_y(); - - let (mut character_chunks, sixel_image_chunks) = - self.grid.read_changes(content_x, content_y); - for character_chunk in character_chunks.iter_mut() { - character_chunk.add_changed_colors(self.grid.changed_colors); - if self - .grid - .selection - .contains_row(character_chunk.y.saturating_sub(content_y)) - { - let background_color = match self.style.colors.bg { - PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), - PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), - }; - character_chunk.add_selection_and_colors( - self.grid.selection, - background_color, - None, - content_x, - content_y, - ); - } else if !self.grid.search_results.selections.is_empty() { - for res in self.grid.search_results.selections.iter() { - if res.contains_row(character_chunk.y.saturating_sub(content_y)) { - let (select_background_palette, select_foreground_palette) = - if Some(res) == self.grid.search_results.active.as_ref() { - (self.style.colors.orange, self.style.colors.black) - } else { - (self.style.colors.green, self.style.colors.black) - }; - let background_color = match select_background_palette { - PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), - PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), - }; - let foreground_color = match select_foreground_palette { - PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb), - PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col), - }; - character_chunk.add_selection_and_colors( - *res, - background_color, - Some(foreground_color), - content_x, - content_y, - ); - } - } - } + match self.grid.render(content_x, content_y, &self.style) { + Ok(rendered_assets) => { + self.set_should_render(false); + return Ok(rendered_assets); + }, + e => return e, } - if self.grid.ring_bell { - let ring_bell = '\u{7}'; - raw_vte_output.push(ring_bell); - self.grid.ring_bell = false; - } - self.set_should_render(false); - Ok(Some(( - character_chunks, - Some(raw_vte_output), - sixel_image_chunks, - ))) } else { Ok(None) } diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 52b28795..cc4e95e6 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -1,15 +1,20 @@ mod pane_resizer; mod tiled_pane_grid; +use crate::resize_pty; +use tiled_pane_grid::{split, TiledPaneGrid}; + use crate::{ os_input_output::ServerOsApi, output::Output, panes::{ActivePanes, PaneId}, tab::{Pane, MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH}, - ui::{boundaries::Boundaries, pane_contents_and_ui::PaneContentsAndUi}, + thread_bus::ThreadSenders, + ui::boundaries::Boundaries, + ui::pane_contents_and_ui::PaneContentsAndUi, + wasm_vm::PluginInstruction, ClientId, }; -use tiled_pane_grid::{split, TiledPaneGrid}; use zellij_utils::{ data::{ModeInfo, Style}, errors::prelude::*, @@ -24,22 +29,6 @@ use std::{ time::Instant, }; -macro_rules! resize_pty { - ($pane:expr, $os_input:expr) => { - if let PaneId::Terminal(ref pid) = $pane.pid() { - // FIXME: This `set_terminal_size_using_terminal_id` call would be best in - // `TerminalPane::reflow_lines` - $os_input.set_terminal_size_using_terminal_id( - *pid, - $pane.get_content_columns() as u16, - $pane.get_content_rows() as u16, - ) - } else { - Ok(()) - } - }; -} - fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (usize, usize) { // (columns_offset, rows_offset) // if the pane is not on the bottom or right edge on the screen, we need to reserve one space @@ -75,6 +64,7 @@ pub struct TiledPanes { panes_to_hide: HashSet, fullscreen_is_active: bool, os_api: Box, + senders: ThreadSenders, } impl TiledPanes { @@ -91,6 +81,7 @@ impl TiledPanes { default_mode_info: ModeInfo, style: Style, os_api: Box, + senders: ThreadSenders, ) -> Self { TiledPanes { panes: BTreeMap::new(), @@ -108,6 +99,7 @@ impl TiledPanes { panes_to_hide: HashSet::new(), fullscreen_is_active: false, os_api, + senders, } } pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box) { @@ -270,7 +262,7 @@ impl TiledPanes { pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset)); } - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } pub fn can_split_pane_horizontally(&mut self, client_id: ClientId) -> bool { @@ -523,7 +515,7 @@ impl TiledPanes { ); pane_grid.resize_pane_left(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -537,7 +529,7 @@ impl TiledPanes { ); pane_grid.resize_pane_right(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -551,7 +543,7 @@ impl TiledPanes { ); pane_grid.resize_pane_up(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -565,7 +557,7 @@ impl TiledPanes { ); pane_grid.resize_pane_down(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -579,7 +571,7 @@ impl TiledPanes { ); pane_grid.resize_increase(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -593,7 +585,7 @@ impl TiledPanes { ); pane_grid.resize_decrease(&active_pane_id); for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api).unwrap(); + resize_pty!(pane, self.os_api, self.senders).unwrap(); } } } @@ -826,7 +818,7 @@ impl TiledPanes { if let Some(geom) = prev_geom_override { new_position.set_geom_override(geom); } - resize_pty!(new_position, self.os_api).unwrap(); + resize_pty!(new_position, self.os_api, self.senders).unwrap(); new_position.set_should_render(true); let current_position = self.panes.get_mut(&active_pane_id).unwrap(); @@ -834,7 +826,7 @@ impl TiledPanes { if let Some(geom) = next_geom_override { current_position.set_geom_override(geom); } - resize_pty!(current_position, self.os_api).unwrap(); + resize_pty!(current_position, self.os_api, self.senders).unwrap(); current_position.set_should_render(true); self.set_pane_frames(self.draw_pane_frames); } @@ -860,7 +852,7 @@ impl TiledPanes { if let Some(geom) = prev_geom_override { new_position.set_geom_override(geom); } - resize_pty!(new_position, self.os_api).unwrap(); + resize_pty!(new_position, self.os_api, self.senders).unwrap(); new_position.set_should_render(true); let current_position = self.panes.get_mut(active_pane_id).unwrap(); @@ -868,7 +860,7 @@ impl TiledPanes { if let Some(geom) = next_geom_override { current_position.set_geom_override(geom); } - resize_pty!(current_position, self.os_api).unwrap(); + resize_pty!(current_position, self.os_api, self.senders).unwrap(); current_position.set_should_render(true); self.set_pane_frames(self.draw_pane_frames); } @@ -896,7 +888,7 @@ impl TiledPanes { if let Some(geom) = prev_geom_override { new_position.set_geom_override(geom); } - resize_pty!(new_position, self.os_api).unwrap(); + resize_pty!(new_position, self.os_api, self.senders).unwrap(); new_position.set_should_render(true); let current_position = self.panes.get_mut(active_pane_id).unwrap(); @@ -904,7 +896,7 @@ impl TiledPanes { if let Some(geom) = next_geom_override { current_position.set_geom_override(geom); } - resize_pty!(current_position, self.os_api).unwrap(); + resize_pty!(current_position, self.os_api, self.senders).unwrap(); current_position.set_should_render(true); self.set_pane_frames(self.draw_pane_frames); } @@ -932,7 +924,7 @@ impl TiledPanes { if let Some(geom) = prev_geom_override { new_position.set_geom_override(geom); } - resize_pty!(new_position, self.os_api).unwrap(); + resize_pty!(new_position, self.os_api, self.senders).unwrap(); new_position.set_should_render(true); let current_position = self.panes.get_mut(active_pane_id).unwrap(); @@ -940,7 +932,7 @@ impl TiledPanes { if let Some(geom) = next_geom_override { current_position.set_geom_override(geom); } - resize_pty!(current_position, self.os_api).unwrap(); + resize_pty!(current_position, self.os_api, self.senders).unwrap(); current_position.set_should_render(true); self.set_pane_frames(self.draw_pane_frames); } @@ -968,7 +960,7 @@ impl TiledPanes { if let Some(geom) = prev_geom_override { new_position.set_geom_override(geom); } - resize_pty!(new_position, self.os_api).unwrap(); + resize_pty!(new_position, self.os_api, self.senders).unwrap(); new_position.set_should_render(true); let current_position = self.panes.get_mut(active_pane_id).unwrap(); @@ -976,7 +968,7 @@ impl TiledPanes { if let Some(geom) = next_geom_override { current_position.set_geom_override(geom); } - resize_pty!(current_position, self.os_api).unwrap(); + resize_pty!(current_position, self.os_api, self.senders).unwrap(); current_position.set_should_render(true); self.set_pane_frames(self.draw_pane_frames); } diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 3f081bf7..ebf72e89 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -30,7 +30,7 @@ pub enum ClientOrTabIndex { /// Instructions related to PTYs (pseudoterminals). #[derive(Clone, Debug)] -pub(crate) enum PtyInstruction { +pub enum PtyInstruction { SpawnTerminal( Option, Option, diff --git a/zellij-server/src/pty_writer.rs b/zellij-server/src/pty_writer.rs index f839b242..9c3b6d49 100644 --- a/zellij-server/src/pty_writer.rs +++ b/zellij-server/src/pty_writer.rs @@ -3,7 +3,7 @@ use zellij_utils::errors::{prelude::*, ContextType, PtyWriteContext}; use crate::thread_bus::Bus; #[derive(Debug, Clone, Eq, PartialEq)] -pub(crate) enum PtyWriteInstruction { +pub enum PtyWriteInstruction { Write(Vec, u32), Exit, } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 01a4b33f..0c03955a 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -118,6 +118,7 @@ type HoldForCommand = Option; #[derive(Debug, Clone)] pub enum ScreenInstruction { PtyBytes(u32, VteBytes), + PluginBytes(u32, ClientId, VteBytes), // u32 is plugin_id Render, NewPane( PaneId, @@ -217,6 +218,7 @@ impl From<&ScreenInstruction> for ScreenContext { fn from(screen_instruction: &ScreenInstruction) -> Self { match *screen_instruction { ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes, + ScreenInstruction::PluginBytes(..) => ScreenContext::PluginBytes, ScreenInstruction::Render => ScreenContext::Render, ScreenInstruction::NewPane(..) => ScreenContext::NewPane, ScreenInstruction::OpenInPlaceEditor(..) => ScreenContext::OpenInPlaceEditor, @@ -531,7 +533,6 @@ impl Screen { }; if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) { - //if let Some(current_tab) = self.get_active_tab(client_id) { match self.get_active_tab(client_id) { Ok(current_tab) => { // If new active tab is same as the current one, do nothing. @@ -1276,6 +1277,17 @@ pub(crate) fn screen_thread_main( } } }, + ScreenInstruction::PluginBytes(pid, client_id, vte_bytes) => { + let all_tabs = screen.get_tabs_mut(); + for tab in all_tabs.values_mut() { + if tab.has_plugin(pid) { + tab.handle_plugin_bytes(pid, client_id, vte_bytes) + .context("failed to process plugin bytes")?; + break; + } + } + screen.render()?; + }, ScreenInstruction::Render => { screen.render()?; }, diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 618c2b35..7ff01bce 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -47,20 +47,27 @@ use zellij_utils::{ pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, }; +#[macro_export] macro_rules! resize_pty { - ($pane:expr, $os_input:expr) => { - if let PaneId::Terminal(ref pid) = $pane.pid() { - // FIXME: This `set_terminal_size_using_terminal_id` call would be best in - // `TerminalPane::reflow_lines` - $os_input.set_terminal_size_using_terminal_id( + ($pane:expr, $os_input:expr, $senders:expr) => {{ + match $pane.pid() { + PaneId::Terminal(ref pid) => $os_input.set_terminal_size_using_terminal_id( *pid, $pane.get_content_columns() as u16, $pane.get_content_rows() as u16, - ) - } else { - Ok(()) + ), + PaneId::Plugin(ref pid) => { + let err_context = || format!("failed to resize plugin {pid}"); + $senders + .send_to_plugin(PluginInstruction::Resize( + *pid, + $pane.get_content_columns(), + $pane.get_content_rows(), + )) + .with_context(err_context) + }, } - }; + }}; } type HoldForCommand = Option; @@ -130,7 +137,8 @@ pub trait Pane { fn reset_size_and_position_override(&mut self); fn set_geom(&mut self, position_and_size: PaneGeom); fn set_geom_override(&mut self, pane_geom: PaneGeom); - fn handle_pty_bytes(&mut self, bytes: VteBytes); + fn handle_pty_bytes(&mut self, _bytes: VteBytes) {} + fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {} fn cursor_coordinates(&self) -> Option<(usize, usize)>; fn adjust_input_to_terminal(&mut self, _input_bytes: Vec) -> Option { None @@ -425,6 +433,7 @@ impl Tab { default_mode_info.clone(), style, os_api.clone(), + senders.clone(), ); let floating_panes = FloatingPanes::new( display_area.clone(), @@ -436,6 +445,7 @@ impl Tab { default_mode_info.clone(), style, os_api.clone(), + senders.clone(), ); let clipboard_provider = match copy_options.command { @@ -525,7 +535,11 @@ impl Tab { let pane_title = run.location.to_string(); self.senders .send_to_plugin(PluginInstruction::Load( - pid_tx, run, tab_index, client_id, + pid_tx, + run, + tab_index, + client_id, + position_and_size.into(), )) .with_context(err_context)?; let pid = pid_rx.recv().with_context(err_context)?; @@ -539,6 +553,12 @@ impl Tab { .clone(), pane_title, layout.name.clone().unwrap_or_default(), + self.sixel_image_store.clone(), + self.terminal_emulator_colors.clone(), + self.terminal_emulator_color_codes.clone(), + self.link_handler.clone(), + self.character_cell_size.clone(), + self.style, ); new_plugin.set_borderless(layout.borderless); self.tiled_panes @@ -779,7 +799,8 @@ impl Tab { embedded_pane_to_float.set_content_offset(Offset::default()); } embedded_pane_to_float.set_geom(new_pane_geom); - resize_pty!(embedded_pane_to_float, self.os_api).with_context(err_context)?; + resize_pty!(embedded_pane_to_float, self.os_api, self.senders) + .with_context(err_context)?; embedded_pane_to_float.set_active_at(Instant::now()); self.floating_panes .add_pane(focused_pane_id, embedded_pane_to_float); @@ -860,7 +881,7 @@ impl Tab { initial_pane_title, ); new_pane.set_content_offset(Offset::frame(1)); // floating panes always have a frame - resize_pty!(new_pane, self.os_api).with_context(err_context)?; + resize_pty!(new_pane, self.os_api, self.senders).with_context(err_context)?; self.floating_panes.add_pane(pid, Box::new(new_pane)); self.floating_panes.focus_pane_for_all_clients(pid); } @@ -934,7 +955,7 @@ impl Tab { self.get_active_pane(client_id) .with_context(|| format!("no active pane found for client {client_id}")) .and_then(|current_active_pane| { - resize_pty!(current_active_pane, self.os_api) + resize_pty!(current_active_pane, self.os_api, self.senders) }) .with_context(err_context)?; }, @@ -1084,6 +1105,16 @@ impl Tab { .values() .any(|s_p| s_p.pid() == PaneId::Terminal(pid)) } + pub fn has_plugin(&self, plugin_id: u32) -> bool { + self.tiled_panes.panes_contain(&PaneId::Plugin(plugin_id)) + || self + .floating_panes + .panes_contain(&PaneId::Plugin(plugin_id)) + || self + .suppressed_panes + .values() + .any(|s_p| s_p.pid() == PaneId::Plugin(plugin_id)) + } pub fn handle_pty_bytes(&mut self, pid: u32, bytes: VteBytes) -> Result<()> { let err_context = || format!("failed to handle pty bytes from fd {pid}"); if let Some(terminal_output) = self @@ -1112,6 +1143,26 @@ impl Tab { } self.process_pty_bytes(pid, bytes).with_context(err_context) } + pub fn handle_plugin_bytes( + &mut self, + pid: u32, + client_id: ClientId, + bytes: VteBytes, + ) -> Result<()> { + if let Some(plugin_pane) = self + .tiled_panes + .get_pane_mut(PaneId::Plugin(pid)) + .or_else(|| self.floating_panes.get_pane_mut(PaneId::Plugin(pid))) + .or_else(|| { + self.suppressed_panes + .values_mut() + .find(|s_p| s_p.pid() == PaneId::Plugin(pid)) + }) + { + plugin_pane.handle_plugin_bytes(client_id, bytes); + } + Ok(()) + } pub fn process_pending_vte_events(&mut self, pid: u32) -> Result<()> { if let Some(pending_vte_events) = self.pending_vte_events.get_mut(&pid) { let vte_events: Vec = pending_vte_events.drain(..).collect(); @@ -1136,7 +1187,8 @@ impl Tab { }) { if self.pids_waiting_resize.remove(&pid) { - resize_pty!(terminal_output, self.os_api).with_context(err_context)?; + resize_pty!(terminal_output, self.os_api, self.senders) + .with_context(err_context)?; } terminal_output.handle_pty_bytes(bytes); let messages_to_pty = terminal_output.drain_messages_to_pty(); @@ -1833,7 +1885,7 @@ impl Tab { // the pane there we replaced. Now, we need to update its pty about its new size. // We couldn't do that before, and we can't use the original moved item now - so we // need to refetch it - resize_pty!(suppressed_pane, self.os_api).unwrap(); + resize_pty!(suppressed_pane, self.os_api, self.senders).unwrap(); } replaced_pane }) diff --git a/zellij-server/src/thread_bus.rs b/zellij-server/src/thread_bus.rs index 0b6311e9..28c3082e 100644 --- a/zellij-server/src/thread_bus.rs +++ b/zellij-server/src/thread_bus.rs @@ -9,7 +9,7 @@ use zellij_utils::{channels, channels::SenderWithContext, errors::ErrorContext}; /// A container for senders to the different threads in zellij on the server side #[derive(Default, Clone)] -pub(crate) struct ThreadSenders { +pub struct ThreadSenders { pub to_screen: Option>, pub to_pty: Option>, pub to_plugin: Option>, diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index f9369b51..dfbaa061 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -37,6 +37,7 @@ use zellij_utils::{ layout::RunPlugin, plugins::{PluginConfig, PluginType, PluginsConfig}, }, + pane_size::Size, serde, }; @@ -95,11 +96,11 @@ folder from the output of the `zellij setup --check` command. } #[derive(Clone, Debug)] -pub(crate) enum PluginInstruction { - Load(Sender, RunPlugin, usize, ClientId), // tx_pid, plugin metadata, tab_index, client_ids +pub enum PluginInstruction { + Load(Sender, RunPlugin, usize, ClientId, Size), // tx_pid, plugin metadata, tab_index, client_ids Update(Option, Option, Event), // Focused plugin / broadcast, client_id, event data - Render(Sender, u32, ClientId, usize, usize), // String buffer, plugin id, client_id, rows, cols - Unload(u32), // plugin_id + Unload(u32), // plugin_id + Resize(u32, usize, usize), // plugin_id, columns, rows AddClient(ClientId), RemoveClient(ClientId), Exit, @@ -110,8 +111,8 @@ impl From<&PluginInstruction> for PluginContext { match *plugin_instruction { PluginInstruction::Load(..) => PluginContext::Load, PluginInstruction::Update(..) => PluginContext::Update, - PluginInstruction::Render(..) => PluginContext::Render, PluginInstruction::Unload(..) => PluginContext::Unload, + PluginInstruction::Resize(..) => PluginContext::Resize, PluginInstruction::Exit => PluginContext::Exit, PluginInstruction::AddClient(_) => PluginContext::AddClient, PluginInstruction::RemoveClient(_) => PluginContext::RemoveClient, @@ -143,7 +144,8 @@ pub(crate) fn wasm_thread_main( let mut plugin_id = 0; let mut headless_plugins = HashMap::new(); - let mut plugin_map: HashMap<(u32, ClientId), (Instance, PluginEnv)> = HashMap::new(); // u32 => pid + let mut plugin_map: HashMap<(u32, ClientId), (Instance, PluginEnv, (usize, usize))> = + HashMap::new(); // u32 => pid, (usize, usize) => rows/columns TODO: clean this up into a struct or something let mut connected_clients: Vec = vec![]; let plugin_dir = data_dir.join("plugins/"); let plugin_global_data_dir = plugin_dir.join("data"); @@ -157,7 +159,7 @@ pub(crate) fn wasm_thread_main( let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Plugin((&event).into())); match event { - PluginInstruction::Load(pid_tx, run, tab_index, client_id) => { + PluginInstruction::Load(pid_tx, run, tab_index, client_id, size) => { let err_context = || format!("failed to load plugin for client {client_id}"); let plugin = plugins @@ -175,7 +177,10 @@ pub(crate) fn wasm_thread_main( let main_user_env = plugin_env.clone(); load_plugin(&mut main_user_instance).with_context(err_context)?; - plugin_map.insert((plugin_id, client_id), (main_user_instance, main_user_env)); + plugin_map.insert( + (plugin_id, client_id), + (main_user_instance, main_user_env, (size.rows, size.cols)), + ); // clone plugins for the rest of the client ids if they exist for client_id in connected_clients.iter() { @@ -190,7 +195,10 @@ pub(crate) fn wasm_thread_main( let mut instance = Instance::new(&module, &zellij.chain_back(wasi)) .with_context(err_context)?; load_plugin(&mut instance).with_context(err_context)?; - plugin_map.insert((plugin_id, *client_id), (instance, new_plugin_env)); + plugin_map.insert( + (plugin_id, *client_id), + (instance, new_plugin_env, (size.rows, size.cols)), + ); } pid_tx.send(plugin_id).with_context(err_context)?; plugin_id += 1; @@ -204,7 +212,9 @@ pub(crate) fn wasm_thread_main( } }; - for (&(plugin_id, client_id), (instance, plugin_env)) in &plugin_map { + for (&(plugin_id, client_id), (instance, plugin_env, (rows, columns))) in + &plugin_map + { let subs = plugin_env .subscriptions .lock() @@ -237,37 +247,24 @@ pub(crate) fn wasm_thread_main( Err(e) => Err(e).with_context(err_context), } })?; + + if *rows > 0 && *columns > 0 { + let render = instance + .exports + .get_function("render") + .with_context(err_context)?; + render + .call(&[Value::I32(*rows as i32), Value::I32(*columns as i32)]) + .with_context(err_context)?; + let rendered_bytes = wasi_read_string(&plugin_env.wasi_env); + drop(bus.senders.send_to_screen(ScreenInstruction::PluginBytes( + plugin_id, + client_id, + rendered_bytes.as_bytes().to_vec(), + ))); + } } } - drop(bus.senders.send_to_screen(ScreenInstruction::Render)); - }, - PluginInstruction::Render(buf_tx, pid, cid, rows, cols) => { - let err_context = || { - format!( - "failed to render plugin with pid {pid} and cid {cid} at ({rows}, {cols})" - ) - }; - - if rows == 0 || cols == 0 { - buf_tx.send(String::new()).with_context(err_context)?; - } else { - let (instance, plugin_env) = plugin_map - .get(&(pid, cid)) - .context("failed to find plugin for rendering") - .with_context(err_context)?; - let render = instance - .exports - .get_function("render") - .with_context(err_context)?; - - render - .call(&[Value::I32(rows as i32), Value::I32(cols as i32)]) - .with_context(err_context)?; - - buf_tx - .send(wasi_read_string(&plugin_env.wasi_env)) - .with_context(err_context)?; - } }, PluginInstruction::Unload(pid) => { info!("Bye from plugin {}", &pid); @@ -279,6 +276,38 @@ pub(crate) fn wasm_thread_main( } } }, + PluginInstruction::Resize(pid, new_columns, new_rows) => { + let err_context = || format!("failed to resize plugin {pid}"); + for ( + (plugin_id, client_id), + (instance, plugin_env, (current_rows, current_columns)), + ) in plugin_map.iter_mut() + { + if *plugin_id == pid { + *current_rows = new_rows; + *current_columns = new_columns; + + // TODO: consolidate with above render function + let render = instance + .exports + .get_function("render") + .with_context(err_context)?; + + render + .call(&[ + Value::I32(*current_rows as i32), + Value::I32(*current_columns as i32), + ]) + .with_context(err_context)?; + let rendered_bytes = wasi_read_string(&plugin_env.wasi_env); + drop(bus.senders.send_to_screen(ScreenInstruction::PluginBytes( + *plugin_id, + *client_id, + rendered_bytes.as_bytes().to_vec(), + ))); + } + } + }, PluginInstruction::AddClient(client_id) => { let err_context = || format!("failed to add plugins for client {client_id}"); @@ -286,7 +315,7 @@ pub(crate) fn wasm_thread_main( let mut seen = HashSet::new(); let mut new_plugins = HashMap::new(); - for (&(plugin_id, _), (instance, plugin_env)) in &plugin_map { + for (&(plugin_id, _), (instance, plugin_env, (rows, columns))) in &plugin_map { if seen.contains(&plugin_id) { continue; } @@ -294,9 +323,14 @@ pub(crate) fn wasm_thread_main( let mut new_plugin_env = plugin_env.clone(); new_plugin_env.client_id = client_id; - new_plugins.insert(plugin_id, (instance.module().clone(), new_plugin_env)); + new_plugins.insert( + plugin_id, + (instance.module().clone(), new_plugin_env, (*rows, *columns)), + ); } - for (plugin_id, (module, mut new_plugin_env)) in new_plugins.drain() { + for (plugin_id, (module, mut new_plugin_env, (rows, columns))) in + new_plugins.drain() + { let wasi = new_plugin_env .wasi_env .import_object(&module) @@ -305,7 +339,10 @@ pub(crate) fn wasm_thread_main( let mut instance = Instance::new(&module, &zellij.chain_back(wasi)) .with_context(err_context)?; load_plugin(&mut instance).with_context(err_context)?; - plugin_map.insert((plugin_id, client_id), (instance, new_plugin_env)); + plugin_map.insert( + (plugin_id, client_id), + (instance, new_plugin_env, (rows, columns)), + ); } // load headless plugins @@ -640,7 +677,8 @@ pub fn wasi_read_string(wasi_env: &WasiEnv) -> String { let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap(); let mut buf = String::new(); wasi_file.read_to_string(&mut buf).unwrap(); - buf + // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c + buf.replace("\n", "\n\r") } pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) { diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 46207ab2..8cd3ab86 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -218,6 +218,7 @@ impl Display for ContextType { #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum ScreenContext { HandlePtyBytes, + PluginBytes, Render, NewPane, OpenInPlaceEditor, @@ -336,6 +337,7 @@ pub enum PluginContext { Update, Render, Unload, + Resize, Exit, AddClient, RemoveClient, diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 908a4dd8..9c64080d 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -176,3 +176,12 @@ impl From for Viewport { } } } + +impl From<&PaneGeom> for Size { + fn from(pane_geom: &PaneGeom) -> Self { + Self { + rows: pane_geom.rows.as_usize(), + cols: pane_geom.cols.as_usize(), + } + } +}