diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 29aa721a..2443c060 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,48 +11,38 @@ env: jobs: build: - name: Build & test + name: Build & Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Add WASM target run: rustup target add wasm32-wasi + - name: Install cargo-make + run: cargo install --debug cargo-make - name: Build - run: ./build-all.sh --verbose - - name: Run tests - run: cargo test -j 1 --verbose - fmt: - name: Rustfmt + run: cargo make build + - name: Test + run: cargo make test + + format: + name: Check Formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + - uses: actions/checkout@v2 + - name: Install cargo-make + run: cargo install --debug cargo-make + - name: Check Format + run: cargo make check-format + clippy: - name: Clippy + name: Check Clippy Lints runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy - - name: Add WASM target - run: rustup target add wasm32-wasi - - name: Build - run: ./build-all.sh --verbose - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-features --all-targets + - uses: actions/checkout@v2 + - name: Install cargo-make + run: cargo install --debug cargo-make + - name: Check Lints + run: cargo make clippy -D clippy::all diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 45af4a8c..6e3833f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,16 +14,30 @@ Before contributing please read our [Code of Conduct](CODE_OF_CONDUCT.md) which all contributors are expected to adhere to. ## Building -To work around a [Cargo bug][https://github.com/rust-lang/cargo/issues/7004], you'll need to use the included `build-all.sh` script. +To build Zellij, we're using cargo-make – you can install it by running `cargo install --force cargo-make`. + +Here are some of the commands currently supported by the build system: ```sh -# An unoptimized debug build -./build-all.sh -# A fully optimized release build -./build-all.sh --release +# Format code, build, then run tests and clippy +cargo make +# You can also perform these actions individually +cargo make format +cargo make build +cargo make test +# Run Zellij (optionally with additional arguments) +cargo make run +cargo make run -- -l strider +# Run Clippy (potentially with additional options) +cargo make clippy +cargo make clippy -W clippy::pedantic +# Install Zellij to some directory +cargo make install /path/of/zellij/binary +# Publish the zellij and zellij-tile crates +cargo make publish ``` -The build script has an optional dependency on `binaryen --version` > 97, for it's command `wasm-opt`. +To run `install` or `publish`, you'll need `binaryen --version` > 97, for it's command `wasm-opt`. ## Looking for something to work on? diff --git a/Cargo.lock b/Cargo.lock index abc7504b..e55d9c43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -207,11 +209,10 @@ dependencies = [ [[package]] name = "bincode" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d175dfa69e619905c4c3cdb7c3c203fa3bdd5d51184e3afdb2742c0280493772" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "byteorder", "serde", ] @@ -255,9 +256,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cache-padded" @@ -320,9 +321,9 @@ dependencies = [ [[package]] name = "console" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" +checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" dependencies = [ "encode_unicode", "lazy_static", @@ -407,9 +408,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -452,9 +453,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" +checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" dependencies = [ "quote", "syn", @@ -462,9 +463,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06d4a9551359071d1890820e3571252b91229e0712e7c36b08940e603c5a8fc" +checksum = "e9d6ddad5866bb2170686ed03f6839d31a76e5407d80b1c334a2c24618543ffa" dependencies = [ "darling_core", "darling_macro", @@ -472,9 +473,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b443e5fb0ddd56e0c9bfa47dc060c5306ee500cb731f2b91432dd65589a77684" +checksum = "a9ced1fd13dc386d5a8315899de465708cf34ee2a6d9394654515214e67bb846" dependencies = [ "fnv", "ident_case", @@ -486,9 +487,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0220073ce504f12a70efc4e7cdaea9e9b1b324872e7ad96a208056d7a638b81" +checksum = "0a7a1445d54b2f9792e3b31a3e715feabbace393f38dc4ffd49d94ee9bc487d5" dependencies = [ "darling_core", "quote", @@ -518,9 +519,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "either" @@ -599,9 +600,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" dependencies = [ "futures-channel", "futures-core", @@ -614,9 +615,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" dependencies = [ "futures-core", "futures-sink", @@ -624,15 +625,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" [[package]] name = "futures-executor" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" +checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" dependencies = [ "futures-core", "futures-task", @@ -641,9 +642,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" [[package]] name = "futures-lite" @@ -662,9 +663,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -674,21 +675,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" [[package]] name = "futures-task" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" [[package]] name = "futures-util" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" dependencies = [ "futures-channel", "futures-core", @@ -818,9 +819,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6cf41e31a7e7b78055b548826da45c7dc74e6a13a3fa6b897a17a01322f26" +checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" dependencies = [ "console", "lazy_static", @@ -893,9 +894,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -936,9 +937,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.88" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libloading" @@ -958,9 +959,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -992,18 +993,18 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap2" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" +checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" dependencies = [ "autocfg", ] @@ -1026,9 +1027,9 @@ checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" [[package]] name = "nb-connect" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f" +checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d" dependencies = [ "libc", "socket2", @@ -1117,11 +1118,11 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "log", "wepoll-sys", @@ -1182,9 +1183,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1347,15 +1348,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scopeguard" version = "1.1.0" @@ -1364,9 +1356,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] @@ -1382,9 +1374,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -1416,9 +1408,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7f3f92a1da3d6b1d32245d0cbcbbab0cfc45996d8df619c42bccfa6d2bbb5f" +checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac" dependencies = [ "libc", "signal-hook-registry", @@ -1453,11 +1445,10 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi", ] @@ -1566,9 +1557,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.62" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2", "quote", @@ -1680,15 +1671,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - [[package]] name = "tracing" version = "0.1.25" @@ -1704,9 +1686,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2", "quote", @@ -1802,9 +1784,9 @@ dependencies = [ [[package]] name = "vec-arena" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" +checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1" [[package]] name = "vec_map" @@ -1814,9 +1796,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "vte" @@ -1854,17 +1836,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" -[[package]] -name = "walkdir" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -1873,9 +1844,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -1883,9 +1854,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1898,9 +1869,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.21" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" +checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1910,9 +1881,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1920,9 +1891,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1933,9 +1904,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "wasmer" @@ -2139,27 +2110,27 @@ checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" [[package]] name = "wast" -version = "35.0.0" +version = "35.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db5ae96da18bb5926341516fd409b5a8ce4e4714da7f0a1063d3b20ac9f9a1e1" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0fa059022c5dabe129f02b429d67086400deb8277f89c975555dacc1dadbcc" +checksum = "8ec280a739b69173e0ffd12c1658507996836ba4e992ed9bc1e5385a0bd72a02" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -2176,12 +2147,12 @@ dependencies = [ [[package]] name = "which" -version = "4.0.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" dependencies = [ + "either", "libc", - "thiserror", ] [[package]] @@ -2200,15 +2171,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2232,7 +2194,7 @@ dependencies = [ [[package]] name = "zellij" -version = "0.2.1" +version = "0.3.3" dependencies = [ "ansi_term 0.12.1", "async-std", @@ -2255,11 +2217,9 @@ dependencies = [ "strum", "termion", "termios", - "toml", "unicode-truncate", "unicode-width", "vte 0.8.0", - "walkdir", "wasmer", "wasmer-wasi", "zellij-tile", @@ -2267,7 +2227,7 @@ dependencies = [ [[package]] name = "zellij-tile" -version = "0.5.0" +version = "0.6.0" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index ee226a9b..30c727b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,12 @@ [package] name = "zellij" -version = "0.2.1" +version = "0.3.3" authors = ["Aram Drevekenin "] edition = "2018" description = "Terminal workspace (WIP)" license = "MIT" repository = "https://github.com/zellij-org/zellij" -[features] -publish = [] - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -37,7 +34,7 @@ lazy_static = "1.4.0" wasmer = "1.0.0" wasmer-wasi = "1.0.0" interprocess = "1.0.1" -zellij-tile = { path = "zellij-tile/", version = "0.5.0" } +zellij-tile = { path = "zellij-tile/", version = "0.6.0" } [dependencies.async-std] version = "1.3.0" @@ -47,10 +44,7 @@ features = ["unstable"] insta = "1.6.0" [build-dependencies] -directories-next = "2.0" structopt = "0.3" -walkdir = "2" -toml = "0.5.8" [workspace] members = [ @@ -58,6 +52,7 @@ members = [ "default-tiles/status-bar", "default-tiles/strider", "default-tiles/tab-bar", + ".", ] [profile.release] diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 00000000..2223c5c3 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,110 @@ +# Global Settings +[env] +CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true +CARGO_TARGET_DIR = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target" +SKIP_TEST = false + +# Add clippy to the default flow +[tasks.dev-test-flow] +dependencies = [ + "format-flow", + "format-toml-conditioned-flow", + "pre-build", + "build", + "post-build", + "test-flow", + "clippy", +] + +# Patching the default flows to skip testing of wasm32-wasi targets +[tasks.pre-test] +condition = { env = { "CARGO_MAKE_CRATE_TARGET_TRIPLE" = "wasm32-wasi" } } +env = { "SKIP_TEST" = true } + +[tasks.test] +condition = { env_false = ["SKIP_TEST"] } +dependencies = ["pre-test"] + +[tasks.post-test] +env = { "SKIP_TEST" = false } + +# Running Zellij using the development data directory +[tasks.run] +workspace = false +dependencies = ["build-workspace", "build-dev-data-dir"] +run_task = "launch" + +[tasks.build-workspace] +run_task = { name = "build", fork = true } + +[tasks.build-dev-data-dir] +script_runner = "@duckscript" +script = ''' +asset_dir = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/assets +target_dir = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target +data_dir = set ${target_dir}/dev-data +rm -r ${data_dir} +cp ${asset_dir}/layouts ${data_dir}/ +plugins = glob_array ${target_dir}/wasm32-wasi/debug/*.wasm +for plugin in ${plugins} + plugin_name = basename ${plugin} + cp ${plugin} ${data_dir}/plugins/${plugin_name} +end +writefile ${data_dir}/VERSION ${CARGO_MAKE_CRATE_VERSION} +''' + +[tasks.launch] +command = "cargo" +args = ["run", "--", "--data-dir", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/dev-data/", "@@split(CARGO_MAKE_TASK_ARGS,;)"] + +# Simple clippy tweak +[tasks.clippy] +args = ["clippy", "--", "@@split(CARGO_MAKE_TASK_ARGS,;)"] + +# Release building and installing Zellij +[tasks.install] +workspace = false +dependencies = ["build-tiles-release", "wasm-opt-tiles", "build-release"] +script_runner = "@duckscript" +script = ''' +if is_dir ${CARGO_MAKE_TASK_ARGS} + trigger_error "You need to specify a full path for the binary, not just a directory!" +else + cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/release/${CARGO_MAKE_CRATE_NAME} ${CARGO_MAKE_TASK_ARGS} +end +''' + +[tasks.build-tiles-release] +env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = ["."] } +run_task = { name = "build-release", fork = true } + +[tasks.wasm-opt-tiles] +script_runner = "@duckscript" +script = ''' +tiles = glob_array ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/wasm32-wasi/release/*.wasm + +for tile in ${tiles} + tile_name = basename ${tile} + tile_out = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/assets/plugins/${tile_name} + if is_path_newer ${tile} ${tile_out} + exec wasm-opt -O ${tile} -o ${tile_out} + end +end +''' + +# Publishing Zellij +[tasks.publish] +clear = true +workspace = false +dependencies = ["build-tiles-release", "wasm-opt-tiles", "build-release", "publish-zellij-tile"] +run_task = "publish-zellij" + +[tasks.publish-zellij-tile] +ignore_errors = true +cwd = "zellij-tile" +command = "cargo" +args = ["publish"] + +[tasks.publish-zellij] +command = "cargo" +args = ["publish"] \ No newline at end of file diff --git a/README.md b/README.md index 203de8cc..af7e1bc4 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,15 @@ Zellij was initially called "Mosaic". ## How to use it? * Clone the project -* In the project folder, run: `./build-all.sh && cargo run` +* Install cargo-make with `cargo install --force cargo-make` +* In the project folder, run: `cargo make run` (note that right now Zellij only supports linux and mac) The status bar on the bottom should guide you through the possible keyboard shortcuts in the app. +For more build commands, take a look at [`Contributing.md`](CONTRIBUTING.md). + # Configuration It is possible to configure keyboard shortcuts and their actions in a yaml file. An example file can be found under `example/config.yaml`. diff --git a/assets/completions/_zellij b/assets/completions/_zellij index 8b89db14..9eecf8e2 100644 --- a/assets/completions/_zellij +++ b/assets/completions/_zellij @@ -20,6 +20,7 @@ _zellij() { '-o+[Send "open file in new pane" to active zellij session]' \ '--open-file=[Send "open file in new pane" to active zellij session]' \ '--max-panes=[Maximum panes on screen, caution: opening more panes will close old ones]' \ +'--data-dir=[Change where zellij looks for layouts and plugins]' \ '-l+[Path to a layout yaml file]' \ '--layout=[Path to a layout yaml file]' \ '-m[Send "move focused pane" to active zellij session]' \ @@ -39,7 +40,27 @@ _zellij() { (( CURRENT += 1 )) curcontext="${curcontext%:*:*}:zellij-command-$line[1]:" case $line[1] in - (config) + (c) +_arguments "${_arguments_options[@]}" \ +'--clean[Disables loading of configuration file at default location]' \ +'-h[Prints help information]' \ +'--help[Prints help information]' \ +'-V[Prints version information]' \ +'--version[Prints version information]' \ +'::path:_files' \ +&& ret=0 +;; +(c) +_arguments "${_arguments_options[@]}" \ +'--clean[Disables loading of configuration file at default location]' \ +'-h[Prints help information]' \ +'--help[Prints help information]' \ +'-V[Prints version information]' \ +'--version[Prints version information]' \ +'::path:_files' \ +&& ret=0 +;; +(config) _arguments "${_arguments_options[@]}" \ '--clean[Disables loading of configuration file at default location]' \ '-h[Prints help information]' \ @@ -70,6 +91,20 @@ _zellij_commands() { ) _describe -t commands 'zellij commands' commands "$@" } +(( $+functions[_c_commands] )) || +_c_commands() { + local commands; commands=( + + ) + _describe -t commands 'c commands' commands "$@" +} +(( $+functions[_zellij__c_commands] )) || +_zellij__c_commands() { + local commands; commands=( + + ) + _describe -t commands 'zellij c commands' commands "$@" +} (( $+functions[_zellij__config_commands] )) || _zellij__config_commands() { local commands; commands=( diff --git a/assets/completions/zellij.bash b/assets/completions/zellij.bash index 549b1e9f..4f28140a 100644 --- a/assets/completions/zellij.bash +++ b/assets/completions/zellij.bash @@ -13,6 +13,9 @@ _zellij() { cmd="zellij" ;; + c) + cmd+="__c" + ;; config) cmd+="__config" ;; @@ -26,7 +29,7 @@ _zellij() { case "${cmd}" in zellij) - opts=" -m -d -h -V -s -o -l --move-focus --debug --help --version --split --open-file --max-panes --layout config help" + opts=" -m -d -h -V -s -o -l --move-focus --debug --help --version --split --open-file --max-panes --data-dir --layout config help c c" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -53,6 +56,10 @@ _zellij() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --data-dir) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --layout) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -69,6 +76,21 @@ _zellij() { return 0 ;; + zellij__c) + opts=" -h -V --clean --help --version " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; zellij__config) opts=" -h -V --clean --help --version " if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/assets/completions/zellij.fish b/assets/completions/zellij.fish index 36bab192..ec758be6 100644 --- a/assets/completions/zellij.fish +++ b/assets/completions/zellij.fish @@ -1,6 +1,7 @@ complete -c zellij -n "__fish_use_subcommand" -s s -l split -d 'Send "split (direction h == horizontal / v == vertical)" to active zellij session' complete -c zellij -n "__fish_use_subcommand" -s o -l open-file -d 'Send "open file in new pane" to active zellij session' complete -c zellij -n "__fish_use_subcommand" -l max-panes -d 'Maximum panes on screen, caution: opening more panes will close old ones' +complete -c zellij -n "__fish_use_subcommand" -l data-dir -d 'Change where zellij looks for layouts and plugins' complete -c zellij -n "__fish_use_subcommand" -s l -l layout -d 'Path to a layout yaml file' complete -c zellij -n "__fish_use_subcommand" -s m -l move-focus -d 'Send "move focused pane" to active zellij session' complete -c zellij -n "__fish_use_subcommand" -s d -l debug diff --git a/assets/layouts/default.yaml b/assets/layouts/default.yaml index 408f2f38..9be7af2a 100644 --- a/assets/layouts/default.yaml +++ b/assets/layouts/default.yaml @@ -6,7 +6,6 @@ parts: Fixed: 1 plugin: tab-bar - direction: Vertical - expansion_boundary: true - direction: Vertical split_size: Fixed: 2 diff --git a/assets/layouts/strider.yaml b/assets/layouts/strider.yaml index 9d0f09b0..5dc9b08f 100644 --- a/assets/layouts/strider.yaml +++ b/assets/layouts/strider.yaml @@ -12,7 +12,6 @@ parts: Percent: 20 plugin: strider - direction: Horizontal - expansion_boundary: true - direction: Vertical split_size: Fixed: 2 diff --git a/assets/plugins/status-bar.wasm b/assets/plugins/status-bar.wasm index 7a5b0e51..445087a2 100644 Binary files a/assets/plugins/status-bar.wasm and b/assets/plugins/status-bar.wasm differ diff --git a/assets/plugins/strider.wasm b/assets/plugins/strider.wasm index ec479353..9aafd410 100644 Binary files a/assets/plugins/strider.wasm and b/assets/plugins/strider.wasm differ diff --git a/assets/plugins/tab-bar.wasm b/assets/plugins/tab-bar.wasm index e895d7ea..3ef4512c 100644 Binary files a/assets/plugins/tab-bar.wasm and b/assets/plugins/tab-bar.wasm differ diff --git a/build-all.sh b/build-all.sh deleted file mode 100755 index f267b110..00000000 --- a/build-all.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -total=5 - -# This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open - -echo "Building status-bar (1/$total)..." -cd default-tiles/status-bar -cargo build --release --target-dir ../../target - -echo "Building strider (2/$total)..." -cd ../strider -cargo build --release --target-dir ../../target - -echo "Building tab-bar (3/$total)..." -cd ../tab-bar -cargo build --release --target-dir ../../target - -echo "Optimising WASM executables (4/$total)..." -cd ../.. -wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm -wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm -wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o target/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm -echo "Building zellij (5/$total)..." -cargo build --target-dir target $@ diff --git a/build.rs b/build.rs index fdb7ab7a..3240db67 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,5 @@ -use directories_next::ProjectDirs; -use std::{ffi::OsStr, fs}; +use std::fs; use structopt::clap::Shell; -use walkdir::WalkDir; include!("src/cli.rs"); @@ -22,32 +20,4 @@ fn main() { clap_app.gen_completions(BIN_NAME, Shell::Bash, &out_dir); clap_app.gen_completions(BIN_NAME, Shell::Zsh, &out_dir); clap_app.gen_completions(BIN_NAME, Shell::Fish, &out_dir); - - // Clear Default Plugins and Layouts - - // Rerun on layout change - for entry in WalkDir::new("assets/layouts") { - let entry = entry.unwrap(); - println!("cargo:rerun-if-changed={}", entry.path().to_string_lossy()); - } - - // Rerun on plugin change - #[cfg(not(feature = "publish"))] - let plugin_dir = "target"; - #[cfg(feature = "publish")] - let plugin_dir = "assets/plugins"; - for entry in WalkDir::new(plugin_dir) { - let entry = entry.unwrap(); - if entry.path().extension() == Some(OsStr::new("wasm")) { - println!("cargo:rerun-if-changed={}", entry.path().to_string_lossy()); - } - } - - let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); - let data_dir = project_dirs.data_dir(); - drop(fs::remove_file(data_dir.join("plugins/status-bar.wasm"))); - drop(fs::remove_file(data_dir.join("plugins/tab-bar.wasm"))); - drop(fs::remove_file(data_dir.join("plugins/strider.wasm"))); - drop(fs::remove_file(data_dir.join("layouts/default.yaml"))); - drop(fs::remove_file(data_dir.join("layouts/strider.yaml"))); } diff --git a/default-tiles/status-bar/src/main.rs b/default-tiles/status-bar/src/main.rs index 8daa158e..ee4d1763 100644 --- a/default-tiles/status-bar/src/main.rs +++ b/default-tiles/status-bar/src/main.rs @@ -63,8 +63,8 @@ impl ZellijTile for State { let second_line = keybinds(&self.mode_info, cols); // [48;5;238m is gray background, [0K is so that it fills the rest of the line - // [48;5;16m is black background, [0K is so that it fills the rest of the line + // [m is background reset, [0K is so that it clears the rest of the line println!("{}\u{1b}[48;5;238m\u{1b}[0K", first_line); - println!("{}\u{1b}[48;5;16m\u{1b}[0K", second_line); + println!("\u{1b}[m{}\u{1b}[0K", second_line); } } diff --git a/default-tiles/status-bar/src/second_line.rs b/default-tiles/status-bar/src/second_line.rs index ebcb93dd..48a82c6c 100644 --- a/default-tiles/status-bar/src/second_line.rs +++ b/default-tiles/status-bar/src/second_line.rs @@ -59,6 +59,122 @@ fn first_word_shortcut(is_first_shortcut: bool, letter: &str, description: &str) len, } } +fn quicknav_full() -> LinePart { + let text_first_part = " Tip: "; + let alt = "Alt"; + let text_second_part = " + "; + let new_pane_shortcut = "n"; + let text_third_part = " => open new pane. "; + let second_alt = "Alt"; + let text_fourth_part = " + "; + let brackets_navigation = "[]"; + let text_fifth_part = " or "; + let hjkl_navigation = "hjkl"; + let text_sixths_part = " => navigate between panes."; + let len = text_first_part.chars().count() + + alt.chars().count() + + text_second_part.chars().count() + + new_pane_shortcut.chars().count() + + text_third_part.chars().count() + + second_alt.chars().count() + + text_fourth_part.chars().count() + + brackets_navigation.chars().count() + + text_fifth_part.chars().count() + + hjkl_navigation.chars().count() + + text_sixths_part.chars().count(); + LinePart { + part: format!( + "{}{}{}{}{}{}{}{}{}{}{}", + text_first_part, + Style::new().fg(ORANGE).bold().paint(alt), + text_second_part, + Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + text_third_part, + Style::new().fg(ORANGE).bold().paint(second_alt), + text_fourth_part, + Style::new().fg(GREEN).bold().paint(brackets_navigation), + text_fifth_part, + Style::new().fg(GREEN).bold().paint(hjkl_navigation), + text_sixths_part, + ), + len, + } +} + +fn quicknav_medium() -> LinePart { + let text_first_part = " Tip: "; + let alt = "Alt"; + let text_second_part = " + "; + let new_pane_shortcut = "n"; + let text_third_part = " => new pane. "; + let second_alt = "Alt"; + let text_fourth_part = " + "; + let brackets_navigation = "[]"; + let text_fifth_part = " or "; + let hjkl_navigation = "hjkl"; + let text_sixths_part = " => navigate."; + let len = text_first_part.chars().count() + + alt.chars().count() + + text_second_part.chars().count() + + new_pane_shortcut.chars().count() + + text_third_part.chars().count() + + second_alt.chars().count() + + text_fourth_part.chars().count() + + brackets_navigation.chars().count() + + text_fifth_part.chars().count() + + hjkl_navigation.chars().count() + + text_sixths_part.chars().count(); + LinePart { + part: format!( + "{}{}{}{}{}{}{}{}{}{}{}", + text_first_part, + Style::new().fg(ORANGE).bold().paint(alt), + text_second_part, + Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + text_third_part, + Style::new().fg(ORANGE).bold().paint(second_alt), + text_fourth_part, + Style::new().fg(GREEN).bold().paint(brackets_navigation), + text_fifth_part, + Style::new().fg(GREEN).bold().paint(hjkl_navigation), + text_sixths_part, + ), + len, + } +} + +fn quicknav_short() -> LinePart { + let text_first_part = " QuickNav: "; + let alt = "Alt"; + let text_second_part = " + "; + let new_pane_shortcut = "n"; + let text_third_part = "/"; + let brackets_navigation = "[]"; + let text_fifth_part = "/"; + let hjkl_navigation = "hjkl"; + let len = text_first_part.chars().count() + + alt.chars().count() + + text_second_part.chars().count() + + new_pane_shortcut.chars().count() + + text_third_part.chars().count() + + brackets_navigation.chars().count() + + text_fifth_part.chars().count() + + hjkl_navigation.chars().count(); + LinePart { + part: format!( + "{}{}{}{}{}{}{}{}", + text_first_part, + Style::new().fg(ORANGE).bold().paint(alt), + text_second_part, + Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + text_third_part, + Style::new().fg(GREEN).bold().paint(brackets_navigation), + text_fifth_part, + Style::new().fg(GREEN).bold().paint(hjkl_navigation), + ), + len, + } +} fn locked_interface_indication() -> LinePart { let locked_text = " -- INTERFACE LOCKED -- "; @@ -99,7 +215,7 @@ fn select_pane_shortcut(is_first_shortcut: bool) -> LinePart { fn full_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { - InputMode::Normal => LinePart::default(), + InputMode::Normal => quicknav_full(), InputMode::Locked => locked_interface_indication(), _ => { let mut line_part = LinePart::default(); @@ -118,7 +234,7 @@ fn full_shortcut_list(help: &ModeInfo) -> LinePart { fn shortened_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { - InputMode::Normal => LinePart::default(), + InputMode::Normal => quicknav_medium(), InputMode::Locked => locked_interface_indication(), _ => { let mut line_part = LinePart::default(); @@ -137,7 +253,14 @@ fn shortened_shortcut_list(help: &ModeInfo) -> LinePart { fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { match help.mode { - InputMode::Normal => LinePart::default(), + InputMode::Normal => { + let line_part = quicknav_short(); + if line_part.len <= max_len { + line_part + } else { + LinePart::default() + } + } InputMode::Locked => { let line_part = locked_interface_indication(); if line_part.len <= max_len { @@ -157,7 +280,7 @@ fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { break; } line_part.len += shortcut.len; - line_part.part = format!("{}{}", line_part.part, shortcut,); + line_part.part = format!("{}{}", line_part.part, shortcut); } let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty()); if line_part.len + select_pane_shortcut.len <= max_len { diff --git a/default-tiles/strider/src/main.rs b/default-tiles/strider/src/main.rs index c2867fe4..71e2e5b7 100644 --- a/default-tiles/strider/src/main.rs +++ b/default-tiles/strider/src/main.rs @@ -23,7 +23,7 @@ impl ZellijTile for State { let next = self.selected().saturating_add(1); *self.selected_mut() = min(self.files.len() - 1, next); } - Key::Right | Key::Char('\n') | Key::Char('l') => { + Key::Right | Key::Char('\n') | Key::Char('l') if !self.files.is_empty() => { match self.files[self.selected()].clone() { FsEntry::Dir(p, _) => { self.path = p; diff --git a/publish.sh b/publish.sh deleted file mode 100755 index d6e22da8..00000000 --- a/publish.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -total=6 - -echo "Building zellij-tile (1/$total)..." -cd zellij-tile -cargo build --release -echo "Building status-bar (2/$total)..." -cd ../default-tiles/status-bar -cargo build --release -echo "Building strider (3/$total)..." -cd ../strider -cargo build --release -echo "Building tab-bar (4/$total)..." -cd ../tab-bar -cargo build --release -echo "Optimising WASM executables (5/$total)..." -cd ../.. -wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o assets/plugins/status-bar.wasm -wasm-opt -O target/wasm32-wasi/release/strider.wasm -o assets/plugins/strider.wasm -wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o assets/plugins/tab-bar.wasm -echo "Publishing zellij (6/$total)..." -cargo publish --features publish $@ diff --git a/rust-toolchain b/rust-toolchain index 89459e2c..09f9a2a1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,8 +1,4 @@ -# https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file [toolchain] -# can be further pinned eg: -# date: "stable-2020-07-10" -# version: "nightly-1.0.0" -channel = "1.49.0" -components = [ "rustfmt", "rust-src", "clippy", "rust-analysis"] +channel = "nightly" +components = ["rustfmt", "clippy", "rust-analysis"] targets = ["wasm32-wasi"] diff --git a/src/cli.rs b/src/cli.rs index d4c0f78a..43047dd3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -20,6 +20,10 @@ pub struct CliArgs { #[structopt(long)] pub max_panes: Option, + /// Change where zellij looks for layouts and plugins + #[structopt(long)] + pub data_dir: Option, + /// Path to a layout yaml file #[structopt(short, long)] pub layout: Option, @@ -34,6 +38,7 @@ pub struct CliArgs { #[derive(Debug, StructOpt)] pub enum ConfigCli { /// Path to the configuration yaml file + #[structopt(alias = "c")] Config { path: Option, #[structopt(long)] diff --git a/src/client/boundaries.rs b/src/client/boundaries.rs index e498da89..28e01a11 100644 --- a/src/client/boundaries.rs +++ b/src/client/boundaries.rs @@ -21,9 +21,9 @@ pub mod boundary_type { pub mod colors { use ansi_term::Colour::{self, Fixed}; - pub const WHITE: Colour = Fixed(255); pub const GREEN: Colour = Fixed(154); pub const GRAY: Colour = Fixed(238); + pub const ORANGE: Colour = Fixed(166); } pub type BoundaryType = &'static str; // easy way to refer to boundary_type above @@ -768,7 +768,7 @@ impl Boundaries { let color = match color.is_some() { true => match input_mode { InputMode::Normal | InputMode::Locked => Some(colors::GREEN), - _ => Some(colors::WHITE), + _ => Some(colors::ORANGE), }, false => None, }; diff --git a/src/client/layout.rs b/src/client/layout.rs index 2d1daaf4..e82f9045 100644 --- a/src/client/layout.rs +++ b/src/client/layout.rs @@ -1,6 +1,5 @@ -use directories_next::ProjectDirs; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{fs::File, io::prelude::*}; use crate::panes::PositionAndSize; @@ -177,16 +176,12 @@ pub struct Layout { pub split_size: Option, #[serde(skip_serializing_if = "Option::is_none")] pub plugin: Option, - #[serde(default)] - pub expansion_boundary: bool, } impl Layout { - pub fn new(layout_path: PathBuf) -> Self { - let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); - let layout_dir = project_dirs.data_dir().join("layouts/"); + pub fn new(layout_path: &Path, data_dir: &Path) -> Self { + let layout_dir = data_dir.join("layouts/"); let mut layout_file = File::open(&layout_path) - .or_else(|_| File::open(&layout_path.with_extension("yaml"))) .or_else(|_| File::open(&layout_dir.join(&layout_path).with_extension("yaml"))) .unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display())); diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs index 007dc33f..f9ec71b8 100644 --- a/src/client/pane_resizer.rs +++ b/src/client/pane_resizer.rs @@ -1,7 +1,10 @@ use crate::os_input_output::OsApi; use crate::panes::{PaneId, PositionAndSize}; use crate::tab::Pane; -use std::collections::{BTreeMap, HashSet}; +use std::{ + cmp::Ordering, + collections::{BTreeMap, HashSet}, +}; pub struct PaneResizer<'a> { panes: &'a mut BTreeMap>, @@ -9,9 +12,7 @@ pub struct PaneResizer<'a> { } // TODO: currently there are some functions here duplicated with Tab -// the reason for this is that we need to get rid of the expansion_boundary -// otherwise we'll have a big separation of concerns issue -// once that is done, all resizing functions should move here +// all resizing functions should move here impl<'a> PaneResizer<'a> { pub fn new( @@ -29,63 +30,72 @@ impl<'a> PaneResizer<'a> { let mut successfully_resized = false; let mut column_difference: isize = 0; let mut row_difference: isize = 0; - if new_size.columns < current_size.columns { - let reduce_by = current_size.columns - new_size.columns; - find_reducible_vertical_chain( - &self.panes, - reduce_by, - current_size.columns, - current_size.rows, - ) - .map(|panes_to_resize| { - self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by); - column_difference = new_size.columns as isize - current_size.columns as isize; - current_size.columns = (current_size.columns as isize + column_difference) as usize; - successfully_resized = true; - }); - } else if new_size.columns > current_size.columns { - let increase_by = new_size.columns - current_size.columns; - find_increasable_vertical_chain( - &self.panes, - increase_by, - current_size.columns, - current_size.rows, - ) - .map(|panes_to_resize| { - self.increase_panes_right_and_push_adjacents_right(panes_to_resize, increase_by); - column_difference = new_size.columns as isize - current_size.columns as isize; - current_size.columns = (current_size.columns as isize + column_difference) as usize; - successfully_resized = true; - }); + match new_size.columns.cmp(¤t_size.columns) { + Ordering::Greater => { + let increase_by = new_size.columns - current_size.columns; + if let Some(panes_to_resize) = find_increasable_vertical_chain( + &self.panes, + increase_by, + current_size.columns, + current_size.rows, + ) { + self.increase_panes_right_and_push_adjacents_right( + panes_to_resize, + increase_by, + ); + column_difference = new_size.columns as isize - current_size.columns as isize; + current_size.columns = + (current_size.columns as isize + column_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Less => { + let reduce_by = current_size.columns - new_size.columns; + if let Some(panes_to_resize) = find_reducible_vertical_chain( + &self.panes, + reduce_by, + current_size.columns, + current_size.rows, + ) { + self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by); + column_difference = new_size.columns as isize - current_size.columns as isize; + current_size.columns = + (current_size.columns as isize + column_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Equal => (), } - if new_size.rows < current_size.rows { - let reduce_by = current_size.rows - new_size.rows; - find_reducible_horizontal_chain( - &self.panes, - reduce_by, - current_size.columns, - current_size.rows, - ) - .map(|panes_to_resize| { - self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by); - row_difference = new_size.rows as isize - current_size.rows as isize; - current_size.rows = (current_size.rows as isize + row_difference) as usize; - successfully_resized = true; - }); - } else if new_size.rows > current_size.rows { - let increase_by = new_size.rows - current_size.rows; - find_increasable_horizontal_chain( - &self.panes, - increase_by, - current_size.columns, - current_size.rows, - ) - .map(|panes_to_resize| { - self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by); - row_difference = new_size.rows as isize - current_size.rows as isize; - current_size.rows = (current_size.rows as isize + row_difference) as usize; - successfully_resized = true; - }); + match new_size.rows.cmp(¤t_size.rows) { + Ordering::Greater => { + let increase_by = new_size.rows - current_size.rows; + if let Some(panes_to_resize) = find_increasable_horizontal_chain( + &self.panes, + increase_by, + current_size.columns, + current_size.rows, + ) { + self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Less => { + let reduce_by = current_size.rows - new_size.rows; + if let Some(panes_to_resize) = find_reducible_horizontal_chain( + &self.panes, + reduce_by, + current_size.columns, + current_size.rows, + ) { + self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Equal => (), } if successfully_resized { Some((column_difference, row_difference)) @@ -229,13 +239,12 @@ impl<'a> PaneResizer<'a> { fn find_next_increasable_horizontal_pane( panes: &BTreeMap>, - right_of: &Box, + right_of: &dyn Pane, increase_by: usize, ) -> Option { let next_pane_candidates = panes.values().filter( |p| { - p.x() == right_of.x() + right_of.columns() + 1 - && p.horizontally_overlaps_with(right_of.as_ref()) + p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) }, // TODO: the name here is wrong, it should be vertically_overlaps_with ); let resizable_candidates = @@ -255,11 +264,11 @@ fn find_next_increasable_horizontal_pane( fn find_next_increasable_vertical_pane( panes: &BTreeMap>, - below: &Box, + below: &dyn Pane, increase_by: usize, ) -> Option { let next_pane_candidates = panes.values().filter( - |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with ); let resizable_candidates = next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by)); @@ -278,11 +287,11 @@ fn find_next_increasable_vertical_pane( fn find_next_reducible_vertical_pane( panes: &BTreeMap>, - below: &Box, + below: &dyn Pane, reduce_by: usize, ) -> Option { let next_pane_candidates = panes.values().filter( - |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with ); let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by)); resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { @@ -300,13 +309,12 @@ fn find_next_reducible_vertical_pane( fn find_next_reducible_horizontal_pane( panes: &BTreeMap>, - right_of: &Box, + right_of: &dyn Pane, reduce_by: usize, ) -> Option { let next_pane_candidates = panes.values().filter( |p| { - p.x() == right_of.x() + right_of.columns() + 1 - && p.horizontally_overlaps_with(right_of.as_ref()) + p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) }, // TODO: the name here is wrong, it should be vertically_overlaps_with ); let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by)); @@ -351,7 +359,11 @@ fn find_increasable_horizontal_chain( if current_pane.x() + current_pane.columns() == screen_width { return Some(panes_to_resize); } - match find_next_increasable_horizontal_pane(panes, ¤t_pane, increase_by) { + match find_next_increasable_horizontal_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { Some(next_pane_id) => { current_pane = panes.get(&next_pane_id).unwrap(); } @@ -397,7 +409,11 @@ fn find_increasable_vertical_chain( if current_pane.y() + current_pane.rows() == screen_height { return Some(panes_to_resize); } - match find_next_increasable_vertical_pane(panes, ¤t_pane, increase_by) { + match find_next_increasable_vertical_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { Some(next_pane_id) => { current_pane = panes.get(&next_pane_id).unwrap(); } @@ -443,7 +459,11 @@ fn find_reducible_horizontal_chain( if current_pane.x() + current_pane.columns() == screen_width { return Some(panes_to_resize); } - match find_next_reducible_horizontal_pane(panes, ¤t_pane, reduce_by) { + match find_next_reducible_horizontal_pane( + panes, + current_pane.as_ref(), + reduce_by, + ) { Some(next_pane_id) => { current_pane = panes.get(&next_pane_id).unwrap(); } @@ -489,7 +509,11 @@ fn find_reducible_vertical_chain( if current_pane.y() + current_pane.rows() == screen_height { return Some(panes_to_resize); } - match find_next_reducible_vertical_pane(panes, ¤t_pane, increase_by) { + match find_next_reducible_vertical_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { Some(next_pane_id) => { current_pane = panes.get(&next_pane_id).unwrap(); } diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs index 7651bdc0..950c72de 100644 --- a/src/client/panes/grid.rs +++ b/src/client/panes/grid.rs @@ -1,9 +1,13 @@ use std::{ cmp::Ordering, + collections::VecDeque, fmt::{self, Debug, Formatter}, }; -static TABSTOP_WIDTH: usize = 8; // TODO: is this always right? +const TABSTOP_WIDTH: usize = 8; // TODO: is this always right? +const SCROLL_BACK: usize = 10_000; + +use crate::utils::logging::debug_log_to_file; use crate::panes::terminal_character::{ CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, @@ -26,14 +30,12 @@ fn get_top_non_canonical_rows(rows: &mut Vec) -> Vec { } } -fn get_bottom_canonical_row_and_wraps(rows: &mut Vec) -> Vec { +fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque) -> Vec { let mut index_of_last_non_canonical_row = None; for (i, row) in rows.iter().enumerate().rev() { + index_of_last_non_canonical_row = Some(i); if row.is_canonical { - index_of_last_non_canonical_row = Some(i); break; - } else { - index_of_last_non_canonical_row = Some(i); } } match index_of_last_non_canonical_row { @@ -45,7 +47,7 @@ fn get_bottom_canonical_row_and_wraps(rows: &mut Vec) -> Vec { } fn transfer_rows_down( - source: &mut Vec, + source: &mut VecDeque, destination: &mut Vec, count: usize, max_src_width: Option, @@ -58,7 +60,7 @@ fn transfer_rows_down( break; } if next_lines.is_empty() { - match source.pop() { + match source.pop_back() { Some(next_line) => { let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(destination); lines_added_to_destination -= top_non_canonical_rows_in_dst.len() as isize; @@ -84,13 +86,12 @@ fn transfer_rows_down( if !next_lines.is_empty() { match max_src_width { Some(max_row_width) => { - let mut excess_rows = - Row::from_rows(next_lines).split_to_rows_of_length(max_row_width); - source.append(&mut excess_rows); + let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width); + source.extend(excess_rows); } None => { let excess_row = Row::from_rows(next_lines); - source.push(excess_row); + bounded_push(source, excess_row); } } } @@ -98,7 +99,7 @@ fn transfer_rows_down( fn transfer_rows_up( source: &mut Vec, - destination: &mut Vec, + destination: &mut VecDeque, count: usize, max_src_width: Option, max_dst_width: Option, @@ -124,7 +125,7 @@ fn transfer_rows_up( break; // no more rows } } - destination.push(next_lines.remove(0)); + bounded_push(destination, next_lines.remove(0)); } if !next_lines.is_empty() { match max_src_width { @@ -142,13 +143,25 @@ fn transfer_rows_up( } } +fn bounded_push(vec: &mut VecDeque, value: Row) { + if vec.len() >= SCROLL_BACK { + vec.pop_front(); + } + vec.push_back(value) +} + #[derive(Clone)] pub struct Grid { - lines_above: Vec, + lines_above: VecDeque, viewport: Vec, lines_below: Vec, + alternative_lines_above_viewport_and_cursor: Option<(VecDeque, Vec, Cursor)>, cursor: Cursor, scroll_region: Option<(usize, usize)>, + pending_styles: CharacterStyles, + pub should_render: bool, + pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "") + pub clear_viewport_before_rendering: bool, pub width: usize, pub height: usize, } @@ -169,13 +182,18 @@ impl Debug for Grid { impl Grid { pub fn new(rows: usize, columns: usize) -> Self { Grid { - lines_above: vec![], + lines_above: VecDeque::with_capacity(SCROLL_BACK), viewport: vec![Row::new().canonical()], lines_below: vec![], cursor: Cursor::new(0, 0), scroll_region: None, width: columns, height: rows, + pending_styles: CharacterStyles::new(), + should_render: true, + cursor_key_mode: false, + alternative_lines_above_viewport_and_cursor: None, + clear_viewport_before_rendering: false, } } pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) { @@ -237,7 +255,7 @@ impl Grid { if !self.lines_above.is_empty() && self.viewport.len() == self.height { let line_to_push_down = self.viewport.pop().unwrap(); self.lines_below.insert(0, line_to_push_down); - let line_to_insert_at_viewport_top = self.lines_above.pop().unwrap(); + let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap(); self.viewport.insert(0, line_to_insert_at_viewport_top); } } @@ -245,11 +263,11 @@ impl Grid { if !self.lines_below.is_empty() && self.viewport.len() == self.height { let mut line_to_push_up = self.viewport.remove(0); if line_to_push_up.is_canonical { - self.lines_above.push(line_to_push_up); + bounded_push(&mut self.lines_above, line_to_push_up); } else { - let mut last_line_above = self.lines_above.pop().unwrap(); + let mut last_line_above = self.lines_above.pop_back().unwrap(); last_line_above.append(&mut line_to_push_up.columns); - self.lines_above.push(last_line_above); + bounded_push(&mut self.lines_above, last_line_above); } let line_to_insert_at_viewport_bottom = self.lines_below.remove(0); self.viewport.push(line_to_insert_at_viewport_bottom); @@ -265,7 +283,7 @@ impl Grid { && viewport_canonical_lines.is_empty() && !self.lines_above.is_empty() { - let mut first_line_above = self.lines_above.pop().unwrap(); + let mut first_line_above = self.lines_above.pop_back().unwrap(); first_line_above.append(&mut row.columns); viewport_canonical_lines.push(first_line_above); cursor_canonical_line_index += 1; @@ -429,13 +447,35 @@ impl Grid { self.scroll_down_one_line(); } } - pub fn rotate_scroll_region_up(&mut self, _count: usize) { - // TBD + pub fn rotate_scroll_region_up(&mut self, count: usize) { + if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region { + for _ in 0..count { + let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width]; + if scroll_region_bottom < self.viewport.len() { + self.viewport.remove(scroll_region_bottom); + } + if scroll_region_top < self.viewport.len() { + self.viewport + .insert(scroll_region_top, Row::from_columns(columns).canonical()); + } + } + } } - pub fn rotate_scroll_region_down(&mut self, _count: usize) { - // TBD + pub fn rotate_scroll_region_down(&mut self, count: usize) { + if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region { + for _ in 0..count { + let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width]; + self.viewport.remove(scroll_region_top); + if self.viewport.len() > scroll_region_top { + self.viewport + .insert(scroll_region_bottom, Row::from_columns(columns).canonical()); + } else { + self.viewport.push(Row::from_columns(columns).canonical()); + } + } + } } - pub fn add_canonical_line(&mut self, pad_character: TerminalCharacter) { + pub fn add_canonical_line(&mut self) { if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region { if self.cursor.y == scroll_region_bottom { // end of scroll region @@ -444,8 +484,12 @@ impl Grid { // then we add an empty line at its end which will be filled by the application // controlling the scroll region (presumably filled by whatever comes next in the // scroll buffer, but that's not something we control) + if scroll_region_top >= self.viewport.len() { + // the state is corrupted + return; + } self.viewport.remove(scroll_region_top); - let columns = vec![pad_character; self.width]; + let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width]; self.viewport .insert(scroll_region_bottom, Row::from_columns(columns).canonical()); return; @@ -482,6 +526,24 @@ impl Grid { } } pub fn insert_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) { + match self.viewport.get_mut(self.cursor.y) { + Some(row) => { + row.insert_character_at(terminal_character, self.cursor.x); + if row.len() > self.width { + row.truncate(self.width); + } + } + None => { + // pad lines until cursor if they do not exist + for _ in self.viewport.len()..self.cursor.y { + self.viewport.push(Row::new().canonical()); + } + self.viewport + .push(Row::new().with_character(terminal_character).canonical()); + } + } + } + pub fn add_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) { match self.viewport.get_mut(self.cursor.y) { Some(row) => row.add_character_at(terminal_character, self.cursor.x), None => { @@ -496,9 +558,7 @@ impl Grid { } pub fn add_character(&mut self, terminal_character: TerminalCharacter) { // TODO: try to separate adding characters from moving the cursors in this function - if self.cursor.x < self.width { - self.insert_character_at_cursor_position(terminal_character); - } else { + if self.cursor.x >= self.width { // line wrap self.cursor.x = 0; if self.cursor.y == self.height - 1 { @@ -519,8 +579,8 @@ impl Grid { self.viewport.push(line_wrapped_row); } } - self.insert_character_at_cursor_position(terminal_character); } + self.add_character_at_cursor_position(terminal_character); self.move_cursor_forward_until_edge(1); } pub fn move_cursor_forward_until_edge(&mut self, count: usize) { @@ -600,7 +660,9 @@ impl Grid { if current_line_index == scroll_region_top { // if we're at the top line, we create a new line and remove the last line that // would otherwise overflow - self.viewport.remove(scroll_region_bottom); + if scroll_region_bottom < self.viewport.len() { + self.viewport.remove(scroll_region_bottom); + } self.viewport.insert(current_line_index, Row::new()); // TODO: .canonical() ? } else if current_line_index > scroll_region_top && current_line_index <= scroll_region_bottom @@ -621,7 +683,7 @@ impl Grid { self.cursor.y + count }; for _ in 0..lines_to_add { - self.add_canonical_line(pad_character); + self.add_canonical_line(); } self.pad_lines_until(self.cursor.y, pad_character); } @@ -663,8 +725,12 @@ impl Grid { for _ in 0..count { self.viewport.remove(current_line_index); let columns = vec![pad_character; self.width]; - self.viewport - .insert(scroll_region_bottom, Row::from_columns(columns).canonical()); + if self.viewport.len() > scroll_region_bottom { + self.viewport + .insert(scroll_region_bottom, Row::from_columns(columns).canonical()); + } else { + self.viewport.push(Row::from_columns(columns).canonical()); + } } } } @@ -683,7 +749,9 @@ impl Grid { // so we add an empty line where the cursor currently is, and delete the last line // of the scroll region for _ in 0..count { - self.viewport.remove(scroll_region_bottom); + if scroll_region_bottom < self.viewport.len() { + self.viewport.remove(scroll_region_bottom); + } let columns = vec![pad_character; self.width]; self.viewport .insert(current_line_index, Row::from_columns(columns).canonical()); @@ -723,6 +791,320 @@ impl Grid { .unwrap() .append(&mut empty_space_to_append); } + fn add_newline(&mut self) { + self.add_canonical_line(); + self.mark_for_rerender(); + } + pub fn mark_for_rerender(&mut self) { + self.should_render = true; + } + fn reset_terminal_state(&mut self) { + self.lines_above = VecDeque::with_capacity(SCROLL_BACK); + self.lines_below = vec![]; + self.viewport = vec![Row::new().canonical()]; + self.alternative_lines_above_viewport_and_cursor = None; + self.cursor_key_mode = false; + self.scroll_region = None; + self.clear_viewport_before_rendering = true; + self.cursor = Cursor::new(0, 0); + } +} + +impl vte::Perform for Grid { + fn print(&mut self, c: char) { + // apparently, building TerminalCharacter like this without a "new" method + // is a little faster + let terminal_character = TerminalCharacter { + character: c, + styles: self.pending_styles, + }; + self.add_character(terminal_character); + } + + fn execute(&mut self, byte: u8) { + match byte { + 8 => { + // backspace + self.move_cursor_backwards(1); + } + 9 => { + // tab + self.advance_to_next_tabstop(self.pending_styles); + } + 10 => { + // 0a, newline + self.add_newline(); + } + 13 => { + // 0d, carriage return + self.move_cursor_to_beginning_of_line(); + } + _ => {} + } + } + + fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) { + // TBD + } + + fn put(&mut self, _byte: u8) { + // TBD + } + + fn unhook(&mut self) { + // TBD + } + + fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) { + // TBD + } + + fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, c: char) { + if c == 'm' { + self.pending_styles.add_style_from_ansi_params(params); + } else if c == 'C' { + // move cursor forward + let move_by = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + self.move_cursor_forward_until_edge(move_by); + } else if c == 'K' { + // clear line (0 => right, 1 => left, 2 => all) + if params[0] == 0 { + let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; + char_to_replace.styles = self.pending_styles; + self.replace_characters_in_line_after_cursor(char_to_replace); + } else if params[0] == 1 { + let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; + char_to_replace.styles = self.pending_styles; + self.replace_characters_in_line_before_cursor(char_to_replace); + } else if params[0] == 2 { + self.clear_cursor_line(); + } + } else if c == 'J' { + // clear all (0 => below, 1 => above, 2 => all, 3 => saved) + let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; + char_to_replace.styles = self.pending_styles; + if params[0] == 0 { + self.clear_all_after_cursor(char_to_replace); + } else if params[0] == 2 { + self.clear_all(char_to_replace); + } + // TODO: implement 1 + } else if c == 'H' { + // goto row/col + // we subtract 1 from the row/column because these are 1 indexed + // (except when they are 0, in which case they should be 1 + // don't look at me, I don't make the rules) + let (row, col) = if params.len() == 1 { + if params[0] == 0 { + (0, params[0] as usize) + } else { + ((params[0] as usize).saturating_sub(1), params[0] as usize) + } + } else if params[0] == 0 { + (0, (params[1] as usize).saturating_sub(1)) + } else { + ( + (params[0] as usize).saturating_sub(1), + (params[1] as usize).saturating_sub(1), + ) + }; + let pad_character = EMPTY_TERMINAL_CHARACTER; + self.move_cursor_to(col, row, pad_character); + } else if c == 'A' { + // move cursor up until edge of screen + let move_up_count = if params[0] == 0 { 1 } else { params[0] }; + self.move_cursor_up(move_up_count as usize); + } else if c == 'B' { + // move cursor down until edge of screen + let move_down_count = if params[0] == 0 { 1 } else { params[0] }; + let pad_character = EMPTY_TERMINAL_CHARACTER; + self.move_cursor_down(move_down_count as usize, pad_character); + } else if c == 'D' { + let move_back_count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + self.move_cursor_back(move_back_count); + } else if c == 'l' { + let first_intermediate_is_questionmark = match _intermediates.get(0) { + Some(b'?') => true, + None => false, + _ => false, + }; + if first_intermediate_is_questionmark { + match params.get(0) { + Some(&1049) => { + if let Some(( + alternative_lines_above, + alternative_viewport, + alternative_cursor, + )) = self.alternative_lines_above_viewport_and_cursor.as_mut() + { + std::mem::swap(&mut self.lines_above, alternative_lines_above); + std::mem::swap(&mut self.viewport, alternative_viewport); + std::mem::swap(&mut self.cursor, alternative_cursor); + } + self.alternative_lines_above_viewport_and_cursor = None; + self.clear_viewport_before_rendering = true; + self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size... + self.mark_for_rerender(); + } + Some(&25) => { + self.hide_cursor(); + self.mark_for_rerender(); + } + Some(&1) => { + self.cursor_key_mode = false; + } + _ => {} + }; + } + } else if c == 'h' { + let first_intermediate_is_questionmark = match _intermediates.get(0) { + Some(b'?') => true, + None => false, + _ => false, + }; + if first_intermediate_is_questionmark { + match params.get(0) { + Some(&25) => { + self.show_cursor(); + self.mark_for_rerender(); + } + Some(&1049) => { + let current_lines_above = std::mem::replace( + &mut self.lines_above, + VecDeque::with_capacity(SCROLL_BACK), + ); + let current_viewport = + std::mem::replace(&mut self.viewport, vec![Row::new().canonical()]); + let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0)); + self.alternative_lines_above_viewport_and_cursor = + Some((current_lines_above, current_viewport, current_cursor)); + self.clear_viewport_before_rendering = true; + } + Some(&1) => { + self.cursor_key_mode = true; + } + _ => {} + }; + } + } else if c == 'r' { + if params.len() > 1 { + // minus 1 because these are 1 indexed + let top_line_index = (params[0] as usize).saturating_sub(1); + let bottom_line_index = (params[1] as usize).saturating_sub(1); + self.set_scroll_region(top_line_index, bottom_line_index); + self.show_cursor(); + } else { + self.clear_scroll_region(); + } + } else if c == 'M' { + // delete lines if currently inside scroll region + let line_count_to_delete = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + let pad_character = EMPTY_TERMINAL_CHARACTER; + self.delete_lines_in_scroll_region(line_count_to_delete, pad_character); + } else if c == 'L' { + // insert blank lines if inside scroll region + let line_count_to_add = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + let pad_character = EMPTY_TERMINAL_CHARACTER; + self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character); + } else if c == 'q' { + // ignore for now to run on mac + } else if c == 'G' { + let column = if params[0] == 0 { + 0 + } else { + params[0] as usize - 1 + }; + self.move_cursor_to_column(column); + } else if c == 'd' { + // goto line + let line = if params[0] == 0 { + 1 + } else { + // minus 1 because this is 1 indexed + params[0] as usize - 1 + }; + let pad_character = EMPTY_TERMINAL_CHARACTER; + self.move_cursor_to_line(line, pad_character); + } else if c == 'P' { + // erase characters + let count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + self.erase_characters(count, self.pending_styles); + } else if c == 'X' { + // erase characters and replace with empty characters of current style + let count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + self.replace_with_empty_chars(count, self.pending_styles); + } else if c == 'T' { + /* + * 124 54 T SD + * Scroll down, new lines inserted at top of screen + * [4T = Scroll down 4, bring previous lines back into view + */ + let line_count: i64 = *params.get(0).expect("A number of lines was expected."); + + if line_count >= 0 { + self.rotate_scroll_region_up(line_count as usize); + } else { + // TODO: can this actually happen? + self.rotate_scroll_region_down(line_count.abs() as usize); + } + } else if c == 'S' { + // move scroll up + let count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + self.rotate_scroll_region_down(count); + } else if c == '@' { + let count = if params[0] == 0 { + 1 + } else { + params[0] as usize + }; + for _ in 0..count { + // TODO: should this be styled? + self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER); + } + } else { + let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)); + } + } + + fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) { + match (byte, intermediates.get(0)) { + (b'M', None) => { + self.move_cursor_up_with_scrolling(1); + } + (b'c', None) => { + self.reset_terminal_state(); + } + _ => {} + } + } } #[derive(Clone)] @@ -786,10 +1168,24 @@ impl Row { } } } + pub fn insert_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) { + match self.columns.len().cmp(&x) { + Ordering::Equal => self.columns.push(terminal_character), + Ordering::Less => { + self.columns.resize(x, EMPTY_TERMINAL_CHARACTER); + self.columns.push(terminal_character); + } + Ordering::Greater => { + self.columns.insert(x, terminal_character); + } + } + } pub fn replace_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) { // this is much more performant than remove/insert - self.columns.push(terminal_character); - self.columns.swap_remove(x); + if x < self.columns.len() { + self.columns.push(terminal_character); + self.columns.swap_remove(x); + } } pub fn replace_columns(&mut self, columns: Vec) { self.columns = columns; @@ -804,7 +1200,11 @@ impl Row { self.columns.append(to_append); } pub fn replace_beginning_with(&mut self, mut line_part: Vec) { - drop(self.columns.drain(0..line_part.len())); + if line_part.len() > self.columns.len() { + self.columns.clear(); + } else { + drop(self.columns.drain(0..line_part.len())); + } line_part.append(&mut self.columns); self.columns = line_part; } @@ -812,7 +1212,9 @@ impl Row { self.columns.len() } pub fn delete_character(&mut self, x: usize) { - self.columns.remove(x); + if x < self.columns.len() { + self.columns.remove(x); + } } pub fn split_to_rows_of_length(&mut self, max_row_length: usize) -> Vec { let mut parts: Vec = vec![]; diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs index caf49acc..24daec08 100644 --- a/src/client/panes/plugin_pane.rs +++ b/src/client/panes/plugin_pane.rs @@ -1,6 +1,4 @@ -#![allow(clippy::clippy::if_same_then_else)] - -use crate::{common::SenderWithContext, pty_bus::VteEvent, tab::Pane, wasm_vm::PluginInstruction}; +use crate::{common::SenderWithContext, pty_bus::VteBytes, tab::Pane, wasm_vm::PluginInstruction}; use std::{sync::mpsc::channel, unimplemented}; @@ -79,7 +77,7 @@ impl Pane for PluginPane { self.position_and_size_override = Some(position_and_size_override); self.should_render = true; } - fn handle_event(&mut self, _event: VteEvent) { + fn handle_pty_bytes(&mut self, _event: VteBytes) { unimplemented!() } fn cursor_coordinates(&self) -> Option<(usize, usize)> { diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs index 053ebd3f..7569e993 100644 --- a/src/client/panes/terminal_character.rs +++ b/src/client/panes/terminal_character.rs @@ -37,10 +37,18 @@ pub enum NamedColor { Magenta, Cyan, White, + BrightBlack, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite, } impl NamedColor { - fn to_foreground_ansi_code(&self) -> String { + fn to_foreground_ansi_code(self) -> String { match self { NamedColor::Black => format!("{}", 30), NamedColor::Red => format!("{}", 31), @@ -50,9 +58,17 @@ impl NamedColor { NamedColor::Magenta => format!("{}", 35), NamedColor::Cyan => format!("{}", 36), NamedColor::White => format!("{}", 37), + NamedColor::BrightBlack => format!("{}", 90), + NamedColor::BrightRed => format!("{}", 91), + NamedColor::BrightGreen => format!("{}", 92), + NamedColor::BrightYellow => format!("{}", 93), + NamedColor::BrightBlue => format!("{}", 94), + NamedColor::BrightMagenta => format!("{}", 95), + NamedColor::BrightCyan => format!("{}", 96), + NamedColor::BrightWhite => format!("{}", 97), } } - fn to_background_ansi_code(&self) -> String { + fn to_background_ansi_code(self) -> String { match self { NamedColor::Black => format!("{}", 40), NamedColor::Red => format!("{}", 41), @@ -62,6 +78,14 @@ impl NamedColor { NamedColor::Magenta => format!("{}", 45), NamedColor::Cyan => format!("{}", 46), NamedColor::White => format!("{}", 47), + NamedColor::BrightBlack => format!("{}", 100), + NamedColor::BrightRed => format!("{}", 101), + NamedColor::BrightGreen => format!("{}", 102), + NamedColor::BrightYellow => format!("{}", 103), + NamedColor::BrightBlue => format!("{}", 104), + NamedColor::BrightMagenta => format!("{}", 105), + NamedColor::BrightCyan => format!("{}", 106), + NamedColor::BrightWhite => format!("{}", 107), } } } @@ -383,6 +407,46 @@ impl CharacterStyles { params_used += 1; // even if it's a bug, let's not create an endless loop, eh? } [49, ..] => *self = self.background(Some(AnsiCode::Reset)), + [90, ..] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) + } + [91, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), + [92, ..] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) + } + [93, ..] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) + } + [94, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))), + [95, ..] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) + } + [96, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))), + [97, ..] => { + *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) + } + [100, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack))) + } + [101, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))), + [102, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen))) + } + [103, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow))) + } + [104, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))) + } + [105, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta))) + } + [106, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))) + } + [107, ..] => { + *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite))) + } _ => { // if this happens, it's a bug let _ = debug_log_to_file(format!("unhandled csi m code {:?}", ansi_params)); diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs index 463a33e3..5d63a100 100644 --- a/src/client/panes/terminal_pane.rs +++ b/src/client/panes/terminal_pane.rs @@ -1,17 +1,13 @@ -#![allow(clippy::clippy::if_same_then_else)] - use crate::tab::Pane; use ::nix::pty::Winsize; use ::std::os::unix::io::RawFd; -use ::vte::Perform; use std::fmt::Debug; use crate::panes::grid::Grid; use crate::panes::terminal_character::{ CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, }; -use crate::utils::logging::debug_log_to_file; -use crate::VteEvent; +use crate::pty_bus::VteBytes; #[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)] pub enum PaneId { @@ -39,19 +35,14 @@ impl From for PositionAndSize { } } -#[derive(Debug)] pub struct TerminalPane { pub grid: Grid, - pub alternative_grid: Option, // for 1049h/l instructions which tell us to switch between these two pub pid: RawFd, - pub should_render: bool, pub selectable: bool, pub position_and_size: PositionAndSize, pub position_and_size_override: Option, - pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "") pub max_height: Option, - pending_styles: CharacterStyles, - clear_viewport_before_rendering: bool, + vte_parser: vte::Parser, } impl Pane for TerminalPane { @@ -86,34 +77,9 @@ impl Pane for TerminalPane { self.position_and_size_override = Some(position_and_size_override); self.reflow_lines(); } - fn handle_event(&mut self, event: VteEvent) { - match event { - VteEvent::Print(c) => { - self.print(c); - self.mark_for_rerender(); - } - VteEvent::Execute(byte) => { - self.execute(byte); - } - VteEvent::Hook(params, intermediates, ignore, c) => { - self.hook(¶ms, &intermediates, ignore, c); - } - VteEvent::Put(byte) => { - self.put(byte); - } - VteEvent::Unhook => { - self.unhook(); - } - VteEvent::OscDispatch(params, bell_terminated) => { - let params: Vec<&[u8]> = params.iter().map(|p| &p[..]).collect(); - self.osc_dispatch(¶ms[..], bell_terminated); - } - VteEvent::CsiDispatch(params, intermediates, ignore, c) => { - self.csi_dispatch(¶ms, &intermediates, ignore, c); - } - VteEvent::EscDispatch(intermediates, ignore, byte) => { - self.esc_dispatch(&intermediates, ignore, byte); - } + fn handle_pty_bytes(&mut self, bytes: VteBytes) { + for byte in bytes.iter() { + self.vte_parser.advance(&mut self.grid, *byte); } } fn cursor_coordinates(&self) -> Option<(usize, usize)> { @@ -128,7 +94,7 @@ impl Pane for TerminalPane { match input_bytes.as_slice() { [27, 91, 68] => { // left arrow - if self.cursor_key_mode { + if self.grid.cursor_key_mode { // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string, // some editors will not show this return "OD".as_bytes().to_vec(); @@ -136,7 +102,7 @@ impl Pane for TerminalPane { } [27, 91, 67] => { // right arrow - if self.cursor_key_mode { + if self.grid.cursor_key_mode { // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string, // some editors will not show this return "OC".as_bytes().to_vec(); @@ -144,7 +110,7 @@ impl Pane for TerminalPane { } [27, 91, 65] => { // up arrow - if self.cursor_key_mode { + if self.grid.cursor_key_mode { // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string, // some editors will not show this return "OA".as_bytes().to_vec(); @@ -152,7 +118,7 @@ impl Pane for TerminalPane { } [27, 91, 66] => { // down arrow - if self.cursor_key_mode { + if self.grid.cursor_key_mode { // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string, // some editors will not show this return "OB".as_bytes().to_vec(); @@ -167,10 +133,10 @@ impl Pane for TerminalPane { self.position_and_size_override } fn should_render(&self) -> bool { - self.should_render + self.grid.should_render } fn set_should_render(&mut self, should_render: bool) { - self.should_render = should_render; + self.grid.should_render = should_render; } fn selectable(&self) -> bool { self.selectable @@ -200,7 +166,7 @@ impl Pane for TerminalPane { let buffer_lines = &self.read_buffer_as_lines(); let display_cols = self.get_columns(); let mut character_styles = CharacterStyles::new(); - if self.clear_viewport_before_rendering { + if self.grid.clear_viewport_before_rendering { for line_index in 0..self.grid.height { let x = self.get_x(); let y = self.get_y(); @@ -213,7 +179,7 @@ impl Pane for TerminalPane { vte_output.push(EMPTY_TERMINAL_CHARACTER.character); } } - self.clear_viewport_before_rendering = false; + self.grid.clear_viewport_before_rendering = false; } for (row, line) in buffer_lines.iter().enumerate() { let x = self.get_x(); @@ -237,7 +203,7 @@ impl Pane for TerminalPane { } character_styles.clear(); } - self.should_render = false; + self.grid.should_render = false; Some(vte_output) } else { None @@ -296,40 +262,31 @@ impl Pane for TerminalPane { } fn scroll_up(&mut self, count: usize) { self.grid.move_viewport_up(count); - self.mark_for_rerender(); + self.grid.should_render = true; } fn scroll_down(&mut self, count: usize) { self.grid.move_viewport_down(count); - self.mark_for_rerender(); + self.grid.should_render = true; } fn clear_scroll(&mut self) { self.grid.reset_viewport(); - self.mark_for_rerender(); + self.grid.should_render = true; } } impl TerminalPane { pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane { let grid = Grid::new(position_and_size.rows, position_and_size.columns); - let pending_styles = CharacterStyles::new(); - TerminalPane { pid, grid, - alternative_grid: None, - should_render: true, selectable: true, - pending_styles, position_and_size, position_and_size_override: None, - cursor_key_mode: false, - clear_viewport_before_rendering: false, max_height: None, + vte_parser: vte::Parser::new(), } } - pub fn mark_for_rerender(&mut self) { - self.should_render = true; - } pub fn get_x(&self) -> usize { match self.position_and_size_override { Some(position_and_size_override) => position_and_size_override.x, @@ -358,11 +315,7 @@ impl TerminalPane { let rows = self.get_rows(); let columns = self.get_columns(); self.grid.change_size(rows, columns); - if let Some(alternative_grid) = self.alternative_grid.as_mut() { - alternative_grid.change_size(rows, columns); - } } - pub fn read_buffer_as_lines(&self) -> Vec> { self.grid.as_character_lines() } @@ -371,322 +324,4 @@ impl TerminalPane { // (x, y) self.grid.cursor_coordinates() } - pub fn rotate_scroll_region_up(&mut self, count: usize) { - self.grid.rotate_scroll_region_up(count); - self.mark_for_rerender(); - } - pub fn rotate_scroll_region_down(&mut self, count: usize) { - self.grid.rotate_scroll_region_down(count); - self.mark_for_rerender(); - } - fn add_newline(&mut self) { - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid.add_canonical_line(pad_character); - self.mark_for_rerender(); - } - fn move_to_beginning_of_line(&mut self) { - self.grid.move_cursor_to_beginning_of_line(); - } - fn move_cursor_backwards(&mut self, count: usize) { - self.grid.move_cursor_backwards(count); - } - fn _reset_all_ansi_codes(&mut self) { - self.pending_styles.clear(); - } -} - -impl vte::Perform for TerminalPane { - fn print(&mut self, c: char) { - // apparently, building TerminalCharacter like this without a "new" method - // is a little faster - let terminal_character = TerminalCharacter { - character: c, - styles: self.pending_styles, - }; - self.grid.add_character(terminal_character); - } - - fn execute(&mut self, byte: u8) { - match byte { - 8 => { - // backspace - self.move_cursor_backwards(1); - } - 9 => { - // tab - self.grid.advance_to_next_tabstop(self.pending_styles); - } - 10 => { - // 0a, newline - self.add_newline(); - } - 13 => { - // 0d, carriage return - self.move_to_beginning_of_line(); - } - _ => {} - } - } - - fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) { - // TBD - } - - fn put(&mut self, _byte: u8) { - // TBD - } - - fn unhook(&mut self) { - // TBD - } - - fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) { - // TBD - } - - fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, c: char) { - if c == 'm' { - self.pending_styles.add_style_from_ansi_params(params); - } else if c == 'C' { - // move cursor forward - let move_by = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - self.grid.move_cursor_forward_until_edge(move_by); - } else if c == 'K' { - // clear line (0 => right, 1 => left, 2 => all) - if params[0] == 0 { - let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; - char_to_replace.styles = self.pending_styles; - self.grid - .replace_characters_in_line_after_cursor(char_to_replace); - } else if params[0] == 1 { - let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; - char_to_replace.styles = self.pending_styles; - self.grid - .replace_characters_in_line_before_cursor(char_to_replace); - } else if params[0] == 2 { - self.grid.clear_cursor_line(); - } - } else if c == 'J' { - // clear all (0 => below, 1 => above, 2 => all, 3 => saved) - let mut char_to_replace = EMPTY_TERMINAL_CHARACTER; - char_to_replace.styles = self.pending_styles; - if params[0] == 0 { - self.grid.clear_all_after_cursor(char_to_replace); - } else if params[0] == 2 { - self.grid.clear_all(char_to_replace); - } - // TODO: implement 1 - } else if c == 'H' { - // goto row/col - // we subtract 1 from the row/column because these are 1 indexed - // (except when they are 0, in which case they should be 1 - // don't look at me, I don't make the rules) - let (row, col) = if params.len() == 1 { - if params[0] == 0 { - (0, params[0] as usize) - } else { - (params[0] as usize - 1, params[0] as usize) - } - } else if params[0] == 0 { - (0, params[1] as usize - 1) - } else { - (params[0] as usize - 1, params[1] as usize - 1) - }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid.move_cursor_to(col, row, pad_character); - } else if c == 'A' { - // move cursor up until edge of screen - let move_up_count = if params[0] == 0 { 1 } else { params[0] }; - self.grid.move_cursor_up(move_up_count as usize); - } else if c == 'B' { - // move cursor down until edge of screen - let move_down_count = if params[0] == 0 { 1 } else { params[0] }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid - .move_cursor_down(move_down_count as usize, pad_character); - } else if c == 'D' { - let move_back_count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - self.grid.move_cursor_back(move_back_count); - } else if c == 'l' { - let first_intermediate_is_questionmark = match _intermediates.get(0) { - Some(b'?') => true, - None => false, - _ => false, - }; - if first_intermediate_is_questionmark { - match params.get(0) { - Some(&1049) => { - if let Some(alternative_grid) = self.alternative_grid.as_mut() { - std::mem::swap(&mut self.grid, alternative_grid); - } - self.alternative_grid = None; - self.clear_viewport_before_rendering = true; - self.mark_for_rerender(); - } - Some(&25) => { - self.grid.hide_cursor(); - self.mark_for_rerender(); - } - Some(&1) => { - self.cursor_key_mode = false; - } - _ => {} - }; - } - } else if c == 'h' { - let first_intermediate_is_questionmark = match _intermediates.get(0) { - Some(b'?') => true, - None => false, - _ => false, - }; - if first_intermediate_is_questionmark { - match params.get(0) { - Some(&25) => { - self.grid.show_cursor(); - self.mark_for_rerender(); - } - Some(&1049) => { - let columns = self - .position_and_size_override - .map(|x| x.columns) - .unwrap_or(self.position_and_size.columns); - let rows = self - .position_and_size_override - .map(|x| x.rows) - .unwrap_or(self.position_and_size.rows); - let current_grid = - std::mem::replace(&mut self.grid, Grid::new(rows, columns)); - self.alternative_grid = Some(current_grid); - self.clear_viewport_before_rendering = true; - } - Some(&1) => { - self.cursor_key_mode = true; - } - _ => {} - }; - } - } else if c == 'r' { - if params.len() > 1 { - // minus 1 because these are 1 indexed - let top_line_index = params[0] as usize - 1; - let bottom_line_index = params[1] as usize - 1; - self.grid - .set_scroll_region(top_line_index, bottom_line_index); - self.grid.show_cursor(); - } else { - self.grid.clear_scroll_region(); - } - } else if c == 't' { - // TBD - title? - } else if c == 'n' { - // TBD - device status report - } else if c == 'c' { - // TBD - identify terminal - } else if c == 'M' { - // delete lines if currently inside scroll region - let line_count_to_delete = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid - .delete_lines_in_scroll_region(line_count_to_delete, pad_character); - } else if c == 'L' { - // insert blank lines if inside scroll region - let line_count_to_add = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid - .add_empty_lines_in_scroll_region(line_count_to_add, pad_character); - } else if c == 'q' { - // ignore for now to run on mac - } else if c == 'G' { - let column = if params[0] == 0 { - 0 - } else { - params[0] as usize - 1 - }; - self.grid.move_cursor_to_column(column); - } else if c == 'd' { - // goto line - let line = if params[0] == 0 { - 1 - } else { - // minus 1 because this is 1 indexed - params[0] as usize - 1 - }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid.move_cursor_to_line(line, pad_character); - } else if c == 'P' { - // erase characters - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - self.grid.erase_characters(count, self.pending_styles); - } else if c == 'X' { - // erase characters and replace with empty characters of current style - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - self.grid - .replace_with_empty_chars(count, self.pending_styles); - } else if c == 'T' { - /* - * 124 54 T SD - * Scroll down, new lines inserted at top of screen - * [4T = Scroll down 4, bring previous lines back into view - */ - let line_count: i64 = *params.get(0).expect("A number of lines was expected."); - - if line_count >= 0 { - self.rotate_scroll_region_up(line_count as usize); - } else { - self.rotate_scroll_region_down(line_count.abs() as usize); - } - } else if c == 'S' { - // move scroll up - let count = if params[0] == 0 { - 1 - } else { - params[0] as usize - }; - let mut pad_character = EMPTY_TERMINAL_CHARACTER; - pad_character.styles = self.pending_styles; - self.grid - .delete_lines_in_scroll_region(count, pad_character); - // TODO: since delete_lines_in_scroll_region also adds lines, is the below redundant? - self.grid - .add_empty_lines_in_scroll_region(count, pad_character); - } else { - let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)); - } - } - - fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) { - if let (b'M', None) = (byte, intermediates.get(0)) { - self.grid.move_cursor_up_with_scrolling(1); - } - } } diff --git a/src/client/tab.rs b/src/client/tab.rs index 695d42cb..8750589f 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -7,7 +7,7 @@ use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContex use crate::layout::Layout; use crate::os_input_output::OsApi; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; -use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::pty_bus::{PtyInstruction, VteBytes}; use crate::utils::shared::adjust_to_size; use crate::wasm_vm::PluginInstruction; use crate::{boundaries::Boundaries, panes::PluginPane}; @@ -68,7 +68,6 @@ pub struct Tab { pub send_pty_instructions: SenderWithContext, pub send_plugin_instructions: SenderWithContext, pub send_app_instructions: SenderWithContext, - expansion_boundary: Option, should_clear_display_before_rendering: bool, pub mode_info: ModeInfo, } @@ -91,7 +90,7 @@ pub trait Pane { fn reset_size_and_position_override(&mut self); fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize); fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize); - fn handle_event(&mut self, event: VteEvent); + fn handle_pty_bytes(&mut self, bytes: VteBytes); fn cursor_coordinates(&self) -> Option<(usize, usize)>; fn adjust_input_to_terminal(&self, input_bytes: Vec) -> Vec; @@ -205,6 +204,7 @@ pub trait Pane { impl Tab { // FIXME: Too many arguments here! Maybe bundle all of the senders for the whole program in a struct? + #[allow(clippy::too_many_arguments)] pub fn new( index: usize, position: usize, @@ -245,7 +245,6 @@ impl Tab { send_app_instructions, send_pty_instructions, send_plugin_instructions, - expansion_boundary: None, should_clear_display_before_rendering: false, mode_info, } @@ -286,9 +285,6 @@ impl Tab { let mut new_pids = new_pids.iter(); for (layout, position_and_size) in positions_and_size { // Just a regular terminal - if layout.expansion_boundary { - self.expansion_boundary = Some(*position_and_size); - } if let Some(plugin) = &layout.plugin { let (pid_tx, pid_rx) = channel(); self.send_plugin_instructions @@ -358,9 +354,7 @@ impl Tab { let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check; let terminal_size = (terminal_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO) * terminal_to_check.columns(); - let terminal_pos_and_size = terminal_to_check.position_and_size(); let terminal_can_be_split = terminal_to_check.columns() >= MIN_TERMINAL_WIDTH - && self.pos_and_size_is_within_expansion_boundary(terminal_pos_and_size) && terminal_to_check.rows() >= MIN_TERMINAL_HEIGHT && ((terminal_to_check.columns() > terminal_to_check.min_width() * 2) || (terminal_to_check.rows() > terminal_to_check.min_height() * 2)); @@ -561,14 +555,17 @@ impl Tab { None } } - pub fn handle_pty_event(&mut self, pid: RawFd, event: VteEvent) { + pub fn has_terminal_pid(&self, pid: RawFd) -> bool { + self.panes.contains_key(&PaneId::Terminal(pid)) + } + pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) { // if we don't have the terminal in self.terminals it's probably because // of a race condition where the terminal was created in pty_bus but has not // yet been created in Screen. These events are currently not buffered, so // if you're debugging seemingly randomly missing stdout data, this is // the reason if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) { - terminal_output.handle_event(event); + terminal_output.handle_pty_bytes(bytes); } } pub fn write_to_active_terminal(&mut self, input_bytes: Vec) { @@ -617,22 +614,29 @@ impl Tab { let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); active_terminal.reset_size_and_position_override(); } else { - let expand_to = self.expansion_boundary.unwrap_or(self.full_screen_ws); let panes = self.get_panes(); - let pane_ids_to_hide = panes.filter_map(|(&id, pane)| { - let position_and_size_for_pane = pane.position_and_size(); - if id != active_pane_id - && self - .pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - { - Some(id) - } else { - None - } - }); + let pane_ids_to_hide = + panes.filter_map( + |(&id, _)| { + if id != active_pane_id { + Some(id) + } else { + None + } + }, + ); self.panes_to_hide = pane_ids_to_hide.collect(); - let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); - active_terminal.override_size_and_position(expand_to.x, expand_to.y, &expand_to); + if self.panes_to_hide.is_empty() { + // nothing to do, pane is already as fullscreen as it can be, let's bail + return; + } else { + let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); + active_terminal.override_size_and_position( + self.full_screen_ws.x, + self.full_screen_ws.y, + &self.full_screen_ws, + ); + } } let active_terminal = self.panes.get(&active_pane_id).unwrap(); if let PaneId::Terminal(active_pid) = active_pane_id { @@ -1484,21 +1488,6 @@ impl Tab { self.increase_pane_width_left(&terminal_id, count); } } - fn pos_and_size_is_within_expansion_boundary(&self, pos_and_size: PositionAndSize) -> bool { - match self.expansion_boundary { - Some(expansion_boundary) => { - pos_and_size.x >= expansion_boundary.x - && pos_and_size.x < expansion_boundary.x + expansion_boundary.columns - && pos_and_size.y >= expansion_boundary.y - && pos_and_size.y < expansion_boundary.y + expansion_boundary.rows - && pos_and_size.x + pos_and_size.columns - <= expansion_boundary.x + expansion_boundary.columns - && pos_and_size.y + pos_and_size.rows - <= expansion_boundary.y + expansion_boundary.rows - } - None => true, // no expansion boundary, no problem - } - } fn can_increase_pane_and_surroundings_right( &self, pane_id: &PaneId, @@ -1514,17 +1503,11 @@ impl Tab { } let mut new_pos_and_size_for_pane = pane.position_and_size(); new_pos_and_size_for_pane.columns += increase_by; - if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) { - return false; - } if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) { return panes_to_the_right.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.columns() > increase_by - && p.columns() - increase_by >= p.min_width() + p.columns() > increase_by && p.columns() - increase_by >= p.min_width() }); } else { false @@ -1545,17 +1528,11 @@ impl Tab { } let mut new_pos_and_size_for_pane = pane.position_and_size(); new_pos_and_size_for_pane.x = new_pos_and_size_for_pane.x.saturating_sub(increase_by); - if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) { - return false; - } if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) { return panes_to_the_left.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.columns() > increase_by - && p.columns() - increase_by >= p.min_width() + p.columns() > increase_by && p.columns() - increase_by >= p.min_width() }); } else { false @@ -1576,17 +1553,11 @@ impl Tab { } let mut new_pos_and_size_for_pane = pane.position_and_size(); new_pos_and_size_for_pane.rows += increase_by; - if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) { - return false; - } if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) { return panes_below.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.rows() > increase_by - && p.rows() - increase_by >= p.min_height() + p.rows() > increase_by && p.rows() - increase_by >= p.min_height() }); } else { false @@ -1603,17 +1574,11 @@ impl Tab { } let mut new_pos_and_size_for_pane = pane.position_and_size(); new_pos_and_size_for_pane.y = new_pos_and_size_for_pane.y.saturating_sub(increase_by); - if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) { - return false; - } if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) { return panes_above.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.rows() > increase_by - && p.rows() - increase_by >= p.min_height() + p.rows() > increase_by && p.rows() - increase_by >= p.min_height() }); } else { false @@ -1630,11 +1595,9 @@ impl Tab { if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) { return panes_to_the_left.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.max_width() - .map(|max_width| p.columns() + reduce_by <= max_width) - .unwrap_or(true) // no max width, increase to your heart's content + p.max_width() + .map(|max_width| p.columns() + reduce_by <= max_width) + .unwrap_or(true) // no max width, increase to your heart's content }); } else { false @@ -1651,11 +1614,9 @@ impl Tab { if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) { return panes_to_the_right.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.max_width() - .map(|max_width| p.columns() + reduce_by <= max_width) - .unwrap_or(true) // no max width, increase to your heart's content + p.max_width() + .map(|max_width| p.columns() + reduce_by <= max_width) + .unwrap_or(true) // no max width, increase to your heart's content }); } else { false @@ -1672,11 +1633,9 @@ impl Tab { if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) { return panes_above.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.max_height() - .map(|max_height| p.rows() + reduce_by <= max_height) - .unwrap_or(true) // no max height, increase to your heart's content + p.max_height() + .map(|max_height| p.rows() + reduce_by <= max_height) + .unwrap_or(true) // no max height, increase to your heart's content }); } else { false @@ -1693,11 +1652,9 @@ impl Tab { if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) { return panes_below.iter().all(|id| { let p = self.panes.get(id).unwrap(); - let position_and_size_for_pane = p.position_and_size(); - self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane) - && p.max_height() - .map(|max_height| p.rows() + reduce_by <= max_height) - .unwrap_or(true) // no max height, increase to your heart's content + p.max_height() + .map(|max_height| p.rows() + reduce_by <= max_height) + .unwrap_or(true) // no max height, increase to your heart's content }); } else { false @@ -1705,27 +1662,18 @@ impl Tab { } pub fn resize_whole_tab(&mut self, new_screen_size: PositionAndSize) { if self.fullscreen_is_active { - // this is not ideal but until we get rid of expansion_boundary, it's a necessity + // this is not ideal, we can improve this self.toggle_active_pane_fullscreen(); } - match PaneResizer::new(&mut self.panes, &mut self.os_api) - .resize(self.full_screen_ws, new_screen_size) + if let Some((column_difference, row_difference)) = + PaneResizer::new(&mut self.panes, &mut self.os_api) + .resize(self.full_screen_ws, new_screen_size) { - Some((column_difference, row_difference)) => { - self.should_clear_display_before_rendering = true; - self.expansion_boundary.as_mut().map(|expansion_boundary| { - // TODO: this is not always accurate - expansion_boundary.columns = - (expansion_boundary.columns as isize + column_difference) as usize; - expansion_boundary.rows = - (expansion_boundary.rows as isize + row_difference) as usize; - }); - self.full_screen_ws.columns = - (self.full_screen_ws.columns as isize + column_difference) as usize; - self.full_screen_ws.rows = - (self.full_screen_ws.rows as isize + row_difference) as usize; - } - None => {} + self.should_clear_display_before_rendering = true; + self.full_screen_ws.columns = + (self.full_screen_ws.columns as isize + column_difference) as usize; + self.full_screen_ws.rows = + (self.full_screen_ws.rows as isize + row_difference) as usize; }; } pub fn resize_left(&mut self) { @@ -1797,6 +1745,62 @@ impl Tab { } self.render(); } + pub fn focus_next_pane(&mut self) { + if !self.has_selectable_panes() { + return; + } + if self.fullscreen_is_active { + return; + } + let active_pane_id = self.get_active_pane_id().unwrap(); + let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let first_pane = panes.get(0).unwrap(); + let active_pane_position = panes + .iter() + .position(|(id, _)| *id == &active_pane_id) // TODO: better + .unwrap(); + if let Some(next_pane) = panes.get(active_pane_position + 1) { + self.active_terminal = Some(*next_pane.0); + } else { + self.active_terminal = Some(*first_pane.0); + } + self.render(); + } + pub fn focus_previous_pane(&mut self) { + if !self.has_selectable_panes() { + return; + } + if self.fullscreen_is_active { + return; + } + let active_pane_id = self.get_active_pane_id().unwrap(); + let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let last_pane = panes.last().unwrap(); + let active_pane_position = panes + .iter() + .position(|(id, _)| *id == &active_pane_id) // TODO: better + .unwrap(); + if active_pane_position == 0 { + self.active_terminal = Some(*last_pane.0); + } else { + self.active_terminal = Some(*panes.get(active_pane_position - 1).unwrap().0); + } + self.render(); + } pub fn move_focus_left(&mut self) { if !self.has_selectable_panes() { return; @@ -2064,47 +2068,79 @@ impl Tab { } } pub fn close_pane_without_rerender(&mut self, id: PaneId) { - if let Some(terminal_to_close) = self.panes.get(&id) { - let terminal_to_close_width = terminal_to_close.columns(); - let terminal_to_close_height = terminal_to_close.rows(); - if let Some(terminals) = self.panes_to_the_left_between_aligning_borders(id) { - for terminal_id in terminals.iter() { - self.increase_pane_width_right(&terminal_id, terminal_to_close_width + 1); - // 1 for the border + if self.fullscreen_is_active { + self.toggle_active_pane_fullscreen(); + } + if let Some(pane_to_close) = self.panes.get(&id) { + let pane_to_close_width = pane_to_close.columns(); + let pane_to_close_height = pane_to_close.rows(); + if let Some(panes) = self.panes_to_the_left_between_aligning_borders(id) { + if panes.iter().all(|p| { + let pane = self.panes.get(p).unwrap(); + pane.can_increase_width_by(pane_to_close_width + 1) + }) { + for pane_id in panes.iter() { + self.increase_pane_width_right(&pane_id, pane_to_close_width + 1); + // 1 for the border + } + self.panes.remove(&id); + if self.active_terminal == Some(id) { + self.active_terminal = self.next_active_pane(panes); + } + return; } - if self.active_terminal == Some(id) { - self.active_terminal = self.next_active_pane(terminals); - } - } else if let Some(terminals) = self.panes_to_the_right_between_aligning_borders(id) { - for terminal_id in terminals.iter() { - self.increase_pane_width_left(&terminal_id, terminal_to_close_width + 1); - // 1 for the border - } - if self.active_terminal == Some(id) { - self.active_terminal = self.next_active_pane(terminals); - } - } else if let Some(terminals) = self.panes_above_between_aligning_borders(id) { - for terminal_id in terminals.iter() { - self.increase_pane_height_down(&terminal_id, terminal_to_close_height + 1); - // 1 for the border - } - if self.active_terminal == Some(id) { - self.active_terminal = self.next_active_pane(terminals); - } - } else if let Some(terminals) = self.panes_below_between_aligning_borders(id) { - for terminal_id in terminals.iter() { - self.increase_pane_height_up(&terminal_id, terminal_to_close_height + 1); - // 1 for the border - } - if self.active_terminal == Some(id) { - self.active_terminal = self.next_active_pane(terminals); - } - } else { } + if let Some(panes) = self.panes_to_the_right_between_aligning_borders(id) { + if panes.iter().all(|p| { + let pane = self.panes.get(p).unwrap(); + pane.can_increase_width_by(pane_to_close_width + 1) + }) { + for pane_id in panes.iter() { + self.increase_pane_width_left(&pane_id, pane_to_close_width + 1); + // 1 for the border + } + self.panes.remove(&id); + if self.active_terminal == Some(id) { + self.active_terminal = self.next_active_pane(panes); + } + return; + } + } + if let Some(panes) = self.panes_above_between_aligning_borders(id) { + if panes.iter().all(|p| { + let pane = self.panes.get(p).unwrap(); + pane.can_increase_height_by(pane_to_close_height + 1) + }) { + for pane_id in panes.iter() { + self.increase_pane_height_down(&pane_id, pane_to_close_height + 1); + // 1 for the border + } + self.panes.remove(&id); + if self.active_terminal == Some(id) { + self.active_terminal = self.next_active_pane(panes); + } + return; + } + } + if let Some(panes) = self.panes_below_between_aligning_borders(id) { + if panes.iter().all(|p| { + let pane = self.panes.get(p).unwrap(); + pane.can_increase_height_by(pane_to_close_height + 1) + }) { + for pane_id in panes.iter() { + self.increase_pane_height_up(&pane_id, pane_to_close_height + 1); + // 1 for the border + } + self.panes.remove(&id); + if self.active_terminal == Some(id) { + self.active_terminal = self.next_active_pane(panes); + } + return; + } + } + // if we reached here, this is either the last pane or there's some sort of + // configuration error (eg. we're trying to close a pane surrounded by fixed panes) self.panes.remove(&id); - if self.active_terminal.is_none() { - self.active_terminal = self.next_active_pane(self.get_pane_ids()); - } } } pub fn close_focused_pane(&mut self) { diff --git a/src/common/command_is_executing.rs b/src/common/command_is_executing.rs index 93c44eb6..775a7bfc 100644 --- a/src/common/command_is_executing.rs +++ b/src/common/command_is_executing.rs @@ -23,7 +23,7 @@ impl CommandIsExecuting { let (lock, cvar) = &*self.closing_pane; let mut closing_pane = lock.lock().unwrap(); *closing_pane = false; - cvar.notify_one(); + cvar.notify_all(); } pub fn opening_new_pane(&mut self) { let (lock, _cvar) = &*self.opening_new_pane; @@ -34,7 +34,7 @@ impl CommandIsExecuting { let (lock, cvar) = &*self.opening_new_pane; let mut opening_new_pane = lock.lock().unwrap(); *opening_new_pane = false; - cvar.notify_one(); + cvar.notify_all(); } pub fn wait_until_pane_is_closed(&self) { let (lock, cvar) = &*self.closing_pane; diff --git a/src/common/errors.rs b/src/common/errors.rs index 00284cd4..d0b748ab 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -1,7 +1,7 @@ //! Error context system based on a thread-local representation of the call stack, itself based on //! the instructions that are sent between threads. -use super::{AppInstruction, OPENCALLS}; +use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS}; use crate::pty_bus::PtyInstruction; use crate::screen::ScreenInstruction; @@ -72,6 +72,12 @@ pub fn handle_panic( } } +pub fn get_current_ctx() -> ErrorContext { + ASYNCOPENCALLS + .try_with(|ctx| *ctx.borrow()) + .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow())) +} + /// A representation of the call stack. #[derive(Clone, Copy)] pub struct ErrorContext { @@ -95,7 +101,9 @@ impl ErrorContext { break; } } - OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self); + ASYNCOPENCALLS + .try_with(|ctx| *ctx.borrow_mut() = *self) + .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self)); } } @@ -168,7 +176,7 @@ impl Display for ContextType { /// Stack call representations corresponding to the different types of [`ScreenInstruction`]s. #[derive(Debug, Clone, Copy, PartialEq)] pub enum ScreenContext { - HandlePtyEvent, + HandlePtyBytes, Render, NewPane, HorizontalSplit, @@ -178,7 +186,9 @@ pub enum ScreenContext { ResizeRight, ResizeDown, ResizeUp, - MoveFocus, + SwitchFocus, + FocusNextPane, + FocusPreviousPane, MoveFocusLeft, MoveFocusDown, MoveFocusUp, @@ -208,7 +218,7 @@ pub enum ScreenContext { impl From<&ScreenInstruction> for ScreenContext { fn from(screen_instruction: &ScreenInstruction) -> Self { match *screen_instruction { - ScreenInstruction::Pty(..) => ScreenContext::HandlePtyEvent, + ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes, ScreenInstruction::Render => ScreenContext::Render, ScreenInstruction::NewPane(_) => ScreenContext::NewPane, ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit, @@ -218,7 +228,9 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::ResizeRight => ScreenContext::ResizeRight, ScreenInstruction::ResizeDown => ScreenContext::ResizeDown, ScreenInstruction::ResizeUp => ScreenContext::ResizeUp, - ScreenInstruction::MoveFocus => ScreenContext::MoveFocus, + ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus, + ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane, + ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane, ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft, ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown, ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp, diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs index ac923f7d..47a5f763 100644 --- a/src/common/input/actions.rs +++ b/src/common/input/actions.rs @@ -24,8 +24,10 @@ pub enum Action { /// Resize focus pane in specified direction. Resize(Direction), /// Switch focus to next pane in specified direction. - SwitchFocus(Direction), + FocusNextPane, + FocusPreviousPane, /// Move the focus pane in specified direction. + SwitchFocus, MoveFocus(Direction), /// Scroll up in focus pane. ScrollUp, diff --git a/src/common/input/config.rs b/src/common/input/config.rs index 0713e54e..1926b11c 100644 --- a/src/common/input/config.rs +++ b/src/common/input/config.rs @@ -51,7 +51,6 @@ impl Config { } /// Deserializes from given path. - #[allow(unused_must_use)] pub fn new(path: &Path) -> ConfigResult { match File::open(path) { Ok(mut file) => { @@ -79,6 +78,7 @@ impl Config { } /// Entry point of the configuration + #[cfg(not(test))] pub fn from_cli_config(cli_config: Option) -> ConfigResult { match cli_config { Some(ConfigCli::Config { clean, .. }) if clean => Ok(Config::default()), @@ -88,6 +88,12 @@ impl Config { Some(_) | None => Ok(Config::from_default_path()?), } } + + /// In order not to mess up tests from changing configurations + #[cfg(test)] + pub fn from_cli_config(_: Option) -> ConfigResult { + Ok(Config::default()) + } } impl Display for ConfigError { diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 2020fc20..0afcab10 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -26,6 +26,7 @@ struct InputHandler { send_pty_instructions: SenderWithContext, send_plugin_instructions: SenderWithContext, send_app_instructions: SenderWithContext, + should_exit: bool, } impl InputHandler { @@ -48,6 +49,7 @@ impl InputHandler { send_pty_instructions, send_plugin_instructions, send_app_instructions, + should_exit: false, } } @@ -56,34 +58,30 @@ impl InputHandler { fn handle_input(&mut self) { let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); err_ctx.add_call(ContextType::StdinHandler); - self.send_pty_instructions.update(err_ctx); - self.send_app_instructions.update(err_ctx); - self.send_screen_instructions.update(err_ctx); let keybinds = self.config.keybinds.clone(); - 'input_loop: loop { - //@@@ I think this should actually just iterate over stdin directly + let alt_left_bracket = vec![27, 91]; + loop { + if self.should_exit { + break; + } let stdin_buffer = self.os_input.read_from_stdin(); for key_result in stdin_buffer.events_and_raw() { match key_result { Ok((event, raw_bytes)) => match event { termion::event::Event::Key(key) => { let key = cast_termion_key(key); - // FIXME this explicit break is needed because the current test - // framework relies on it to not create dead threads that loop - // and eat up CPUs. Do not remove until the test framework has - // been revised. Sorry about this (@categorille) - let mut should_break = false; - for action in - Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds) - { - should_break |= self.dispatch_action(action); - } - if should_break { - break 'input_loop; + self.handle_key(&key, raw_bytes, &keybinds); + } + termion::event::Event::Unsupported(unsupported_key) => { + // we have to do this because of a bug in termion + // this should be a key event and not an unsupported event + if unsupported_key == alt_left_bracket { + let key = Key::Alt('['); + self.handle_key(&key, raw_bytes, &keybinds); } } - termion::event::Event::Mouse(_) | termion::event::Event::Unsupported(_) => { - // Mouse and unsupported events aren't implemented yet, + termion::event::Event::Mouse(_) => { + // Mouse events aren't implemented yet, // use a NoOp untill then. } }, @@ -92,6 +90,14 @@ impl InputHandler { } } } + fn handle_key(&mut self, key: &Key, raw_bytes: Vec, keybinds: &Keybinds) { + for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds) { + let should_exit = self.dispatch_action(action); + if should_exit { + self.should_exit = true; + } + } + } /// Dispatches an [`Action`]. /// @@ -144,9 +150,19 @@ impl InputHandler { }; self.send_screen_instructions.send(screen_instr).unwrap(); } - Action::SwitchFocus(_) => { + Action::SwitchFocus => { self.send_screen_instructions - .send(ScreenInstruction::MoveFocus) + .send(ScreenInstruction::SwitchFocus) + .unwrap(); + } + Action::FocusNextPane => { + self.send_screen_instructions + .send(ScreenInstruction::FocusNextPane) + .unwrap(); + } + Action::FocusPreviousPane => { + self.send_screen_instructions + .send(ScreenInstruction::FocusPreviousPane) .unwrap(); } Action::MoveFocus(direction) => { diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs index 653213a7..3b57dc02 100644 --- a/src/common/input/keybinds.rs +++ b/src/common/input/keybinds.rs @@ -129,6 +129,14 @@ impl Keybinds { vec![Action::SwitchToMode(InputMode::Scroll)], ); defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); + + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } InputMode::Locked => { defaults.insert( @@ -171,6 +179,14 @@ impl Keybinds { defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]); defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]); defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]); + + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } InputMode::Pane => { defaults.insert( @@ -211,7 +227,7 @@ impl Keybinds { defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]); defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Char('p'), vec![Action::SwitchFocus(Direction::Right)]); + defaults.insert(Key::Char('p'), vec![Action::SwitchFocus]); defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]); defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]); defaults.insert( @@ -220,6 +236,14 @@ impl Keybinds { ); defaults.insert(Key::Char('x'), vec![Action::CloseFocus]); defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]); + + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } InputMode::Tab => { defaults.insert( @@ -278,6 +302,13 @@ impl Keybinds { for i in '1'..='9' { defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]); } + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } InputMode::Scroll => { defaults.insert( @@ -310,6 +341,14 @@ impl Keybinds { defaults.insert(Key::Down, vec![Action::ScrollDown]); defaults.insert(Key::Up, vec![Action::ScrollUp]); + + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } InputMode::RenameTab => { defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]); @@ -324,6 +363,14 @@ impl Keybinds { Action::SwitchToMode(InputMode::Tab), ], ); + + defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); + defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); + defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); + defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); + defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); + defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); + defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); } } ModeKeybinds(defaults) diff --git a/src/common/install.rs b/src/common/install.rs index 8b070848..86fdd269 100644 --- a/src/common/install.rs +++ b/src/common/install.rs @@ -1,12 +1,41 @@ +use std::{fs, path::Path}; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + #[macro_export] macro_rules! asset_map { ($($src:literal => $dst:literal),+ $(,)?) => { { let mut assets = std::collections::HashMap::new(); $( - assets.insert($dst, include_bytes!(concat!("../", $src)).to_vec()); + assets.insert($dst, include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $src)).to_vec()); )+ assets } } } + +pub fn populate_data_dir(data_dir: &Path) { + // First run installation of default plugins & layouts + let mut assets = asset_map! { + "assets/layouts/default.yaml" => "layouts/default.yaml", + "assets/layouts/strider.yaml" => "layouts/strider.yaml", + }; + assets.extend(asset_map! { + "assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm", + "assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm", + "assets/plugins/strider.wasm" => "plugins/strider.wasm", + }); + assets.insert("VERSION", VERSION.as_bytes().to_vec()); + + let last_version = fs::read_to_string(data_dir.join("VERSION")).unwrap_or_default(); + let out_of_date = VERSION != last_version; + + for (path, bytes) in assets { + let path = data_dir.join(path); + fs::create_dir_all(path.parent().unwrap()).unwrap(); + if out_of_date || !path.exists() { + fs::write(path, bytes).expect("Failed to install default assets!"); + } + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index e9b74d58..860d12db 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -25,10 +25,15 @@ use crate::cli::CliArgs; use crate::common::input::config::Config; use crate::layout::Layout; use crate::panes::PaneId; +use async_std::task_local; use command_is_executing::CommandIsExecuting; use directories_next::ProjectDirs; -use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext}; +use errors::{ + get_current_ctx, AppContext, ContextType, ErrorContext, PluginContext, PtyContext, + ScreenContext, +}; use input::handler::input_loop; +use install::populate_data_dir; use os_input_output::OsApi; use pty_bus::{PtyBus, PtyInstruction}; use screen::{Screen, ScreenInstruction}; @@ -72,32 +77,23 @@ enum SenderType { /// synchronously or asynchronously depending on the underlying [`SenderType`]. #[derive(Clone)] pub struct SenderWithContext { - err_ctx: ErrorContext, sender: SenderType, } impl SenderWithContext { - fn new(err_ctx: ErrorContext, sender: SenderType) -> Self { - Self { err_ctx, sender } + fn new(sender: SenderType) -> Self { + Self { sender } } /// Sends an event, along with the current [`ErrorContext`], on this /// [`SenderWithContext`]'s channel. pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> { + let err_ctx = get_current_ctx(); match self.sender { - SenderType::Sender(ref s) => s.send((event, self.err_ctx)), - SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)), + SenderType::Sender(ref s) => s.send((event, err_ctx)), + SenderType::SyncSender(ref s) => s.send((event, err_ctx)), } } - - /// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds - /// a call to the error context. - /// - /// Updating [`ErrorContext`]s works in this way so that these contexts are only ever - /// allocated on the stack (which is thread-specific), and not on the heap. - pub fn update(&mut self, new_ctx: ErrorContext) { - self.err_ctx = new_ctx; - } } unsafe impl Send for SenderWithContext {} @@ -109,6 +105,12 @@ thread_local!( static OPENCALLS: RefCell = RefCell::default() ); +task_local! { + /// A key to some task local storage that holds a representation of the task's call + /// stack in the form of an [`ErrorContext`]. + static ASYNCOPENCALLS: RefCell = RefCell::default() +} + /// Instructions related to the entire application. #[derive(Clone)] pub enum AppInstruction { @@ -140,25 +142,23 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let (send_screen_instructions, receive_screen_instructions): ChannelWithContext< ScreenInstruction, > = mpsc::channel(); - let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); - let mut send_screen_instructions = - SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions)); + let send_screen_instructions = + SenderWithContext::new(SenderType::Sender(send_screen_instructions)); let (send_pty_instructions, receive_pty_instructions): ChannelWithContext = mpsc::channel(); - let mut send_pty_instructions = - SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions)); + let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions)); let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext< PluginInstruction, > = mpsc::channel(); let send_plugin_instructions = - SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions)); + SenderWithContext::new(SenderType::Sender(send_plugin_instructions)); let (send_app_instructions, receive_app_instructions): SyncChannelWithContext = mpsc::sync_channel(0); let send_app_instructions = - SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions)); + SenderWithContext::new(SenderType::SyncSender(send_app_instructions)); let mut pty_bus = PtyBus::new( receive_pty_instructions, @@ -168,12 +168,22 @@ pub fn start(mut os_input: Box, opts: CliArgs) { opts.debug, ); + // Determine and initialize the data directory + let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); + let data_dir = opts + .data_dir + .unwrap_or_else(|| project_dirs.data_dir().to_path_buf()); + populate_data_dir(&data_dir); + // Don't use default layouts in tests, but do everywhere else #[cfg(not(test))] let default_layout = Some(PathBuf::from("default")); #[cfg(test)] let default_layout = None; - let maybe_layout = opts.layout.or(default_layout).map(Layout::new); + let maybe_layout = opts + .layout + .or(default_layout) + .map(|p| Layout::new(&p, &data_dir)); #[cfg(not(test))] std::panic::set_hook({ @@ -195,7 +205,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .recv() .expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Pty(PtyContext::from(&event))); - pty_bus.send_screen_instructions.update(err_ctx); match event { PtyInstruction::SpawnTerminal(file_to_open) => { let pid = pty_bus.spawn_terminal(file_to_open); @@ -272,14 +281,24 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .recv() .expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event))); - screen.send_app_instructions.update(err_ctx); - screen.send_pty_instructions.update(err_ctx); match event { - ScreenInstruction::Pty(pid, vte_event) => { - screen - .get_active_tab_mut() - .unwrap() - .handle_pty_event(pid, vte_event); + ScreenInstruction::PtyBytes(pid, vte_bytes) => { + let active_tab = screen.get_active_tab_mut().unwrap(); + if active_tab.has_terminal_pid(pid) { + // it's most likely that this event is directed at the active tab + // look there first + active_tab.handle_pty_bytes(pid, vte_bytes); + } else { + // if this event wasn't directed at the active tab, start looking + // in other tabs + let all_tabs = screen.get_tabs_mut(); + for tab in all_tabs.values_mut() { + if tab.has_terminal_pid(pid) { + tab.handle_pty_bytes(pid, vte_bytes); + break; + } + } + } } ScreenInstruction::Render => { screen.render(); @@ -314,9 +333,15 @@ pub fn start(mut os_input: Box, opts: CliArgs) { ScreenInstruction::ResizeUp => { screen.get_active_tab_mut().unwrap().resize_up(); } - ScreenInstruction::MoveFocus => { + ScreenInstruction::SwitchFocus => { screen.get_active_tab_mut().unwrap().move_focus(); } + ScreenInstruction::FocusNextPane => { + screen.get_active_tab_mut().unwrap().focus_next_pane(); + } + ScreenInstruction::FocusPreviousPane => { + screen.get_active_tab_mut().unwrap().focus_previous_pane(); + } ScreenInstruction::MoveFocusLeft => { screen.get_active_tab_mut().unwrap().move_focus_left(); } @@ -415,9 +440,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let wasm_thread = thread::Builder::new() .name("wasm".to_string()) .spawn({ - let mut send_pty_instructions = send_pty_instructions.clone(); - let mut send_screen_instructions = send_screen_instructions.clone(); - let mut send_app_instructions = send_app_instructions.clone(); + let send_pty_instructions = send_pty_instructions.clone(); + let send_screen_instructions = send_screen_instructions.clone(); + let send_app_instructions = send_app_instructions.clone(); let store = Store::default(); let mut plugin_id = 0; @@ -427,14 +452,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .recv() .expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event))); - send_screen_instructions.update(err_ctx); - send_pty_instructions.update(err_ctx); - send_app_instructions.update(err_ctx); match event { PluginInstruction::Load(pid_tx, path) => { - let project_dirs = - ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); - let plugin_dir = project_dirs.data_dir().join("plugins/"); + let plugin_dir = data_dir.join("plugins/"); let wasm_bytes = fs::read(&path) .or_else(|_| fs::read(&path.with_extension("wasm"))) .or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm"))) @@ -538,16 +558,14 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .name("ipc_server".to_string()) .spawn({ use std::io::Read; - let mut send_pty_instructions = send_pty_instructions.clone(); - let mut send_screen_instructions = send_screen_instructions.clone(); + let send_pty_instructions = send_pty_instructions.clone(); + let send_screen_instructions = send_screen_instructions.clone(); move || { std::fs::remove_file(ZELLIJ_IPC_PIPE).ok(); let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE) .expect("could not listen on ipc socket"); let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); err_ctx.add_call(ContextType::IpcServer); - send_pty_instructions.update(err_ctx); - send_screen_instructions.update(err_ctx); for stream in listener.incoming() { match stream { @@ -577,7 +595,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { } ApiCommand::MoveFocus => { send_screen_instructions - .send(ScreenInstruction::MoveFocus) + .send(ScreenInstruction::FocusNextPane) .unwrap(); } } @@ -619,8 +637,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .expect("failed to receive app instruction on channel"); err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction))); - send_screen_instructions.update(err_ctx); - send_pty_instructions.update(err_ctx); match app_instruction { AppInstruction::Exit => { break; @@ -634,7 +650,11 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let _ = wasm_thread.join(); os_input.unset_raw_mode(0); let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1); - let error = format!("{}\n{}", goto_start_of_last_line, backtrace); + let restore_snapshot = "\u{1b}[?1049l"; + let error = format!( + "{}\n{}{}", + goto_start_of_last_line, restore_snapshot, backtrace + ); let _ = os_input .get_stdout_writer() .write(error.as_bytes()) diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index de0c6a3b..35d77fdd 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -84,15 +84,10 @@ fn handle_command_exit(mut child: Child) { } for signal in signals.pending() { - // FIXME: We need to handle more signals here! - #[allow(clippy::single_match)] - match signal { - SIGINT => { - child.kill().unwrap(); - child.wait().unwrap(); - break 'handle_exit; - } - _ => {} + if let SIGINT = signal { + child.kill().unwrap(); + child.wait().unwrap(); + break 'handle_exit; } } } @@ -242,7 +237,13 @@ impl OsApi for OsInputOutput { Box::new(stdout) } fn kill(&mut self, pid: RawFd) -> Result<(), nix::Error> { - kill(Pid::from_raw(pid), Some(Signal::SIGINT)).unwrap(); + // TODO: + // Ideally, we should be using SIGINT rather than SIGKILL here, but there are cases in which + // the terminal we're trying to kill hangs on SIGINT and so all the app gets stuck + // that's why we're sending SIGKILL here + // A better solution would be to send SIGINT here and not wait for it, and then have + // a background thread do the waitpid stuff and send SIGKILL if the process is stuck + kill(Pid::from_raw(pid), Some(Signal::SIGKILL)).unwrap(); waitpid(Pid::from_raw(pid), None).unwrap(); Ok(()) } diff --git a/src/common/pty_bus.rs b/src/common/pty_bus.rs index 7e854257..2c132228 100644 --- a/src/common/pty_bus.rs +++ b/src/common/pty_bus.rs @@ -6,14 +6,13 @@ use ::std::os::unix::io::RawFd; use ::std::pin::*; use ::std::sync::mpsc::Receiver; use ::std::time::{Duration, Instant}; -use ::vte; use std::path::PathBuf; -use super::{ScreenInstruction, SenderWithContext, OPENCALLS}; +use super::{ScreenInstruction, SenderWithContext}; use crate::os_input_output::OsApi; use crate::utils::logging::debug_to_file; use crate::{ - errors::{ContextType, ErrorContext}, + errors::{get_current_ctx, ContextType, ErrorContext}, panes::PaneId, }; use crate::{layout::Layout, wasm_vm::PluginInstruction}; @@ -44,7 +43,7 @@ impl Stream for ReadFromPid { // indicates end of file Poll::Ready(None) } else { - let res = Some(read_buffer[..=*res].to_vec()); + let res = Some(read_buffer[..*res].to_vec()); Poll::Ready(res) } } @@ -64,86 +63,7 @@ impl Stream for ReadFromPid { } } -#[derive(Debug, Clone)] -pub enum VteEvent { - // TODO: try not to allocate Vecs - Print(char), - Execute(u8), // byte - Hook(Vec, Vec, bool, char), // params, intermediates, ignore, char - Put(u8), // byte - Unhook, - OscDispatch(Vec>, bool), // params, bell_terminated - CsiDispatch(Vec, Vec, bool, char), // params, intermediates, ignore, char - EscDispatch(Vec, bool, u8), // intermediates, ignore, byte -} - -struct VteEventSender { - id: RawFd, - sender: SenderWithContext, -} - -impl VteEventSender { - pub fn new(id: RawFd, sender: SenderWithContext) -> Self { - VteEventSender { id, sender } - } -} - -impl vte::Perform for VteEventSender { - fn print(&mut self, c: char) { - let _ = self - .sender - .send(ScreenInstruction::Pty(self.id, VteEvent::Print(c))); - } - fn execute(&mut self, byte: u8) { - let _ = self - .sender - .send(ScreenInstruction::Pty(self.id, VteEvent::Execute(byte))); - } - - fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) { - let params = params.iter().copied().collect(); - let intermediates = intermediates.iter().copied().collect(); - let instruction = - ScreenInstruction::Pty(self.id, VteEvent::Hook(params, intermediates, ignore, c)); - let _ = self.sender.send(instruction); - } - - fn put(&mut self, byte: u8) { - let _ = self - .sender - .send(ScreenInstruction::Pty(self.id, VteEvent::Put(byte))); - } - - fn unhook(&mut self) { - let _ = self - .sender - .send(ScreenInstruction::Pty(self.id, VteEvent::Unhook)); - } - - fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { - let params = params.iter().map(|p| p.to_vec()).collect(); - let instruction = - ScreenInstruction::Pty(self.id, VteEvent::OscDispatch(params, bell_terminated)); - let _ = self.sender.send(instruction); - } - - fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) { - let params = params.iter().copied().collect(); - let intermediates = intermediates.iter().copied().collect(); - let instruction = ScreenInstruction::Pty( - self.id, - VteEvent::CsiDispatch(params, intermediates, ignore, c), - ); - let _ = self.sender.send(instruction); - } - - fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { - let intermediates = intermediates.iter().copied().collect(); - let instruction = - ScreenInstruction::Pty(self.id, VteEvent::EscDispatch(intermediates, ignore, byte)); - let _ = self.sender.send(instruction); - } -} +pub type VteBytes = Vec; /// Instructions related to PTYs (pseudoterminals). #[derive(Clone, Debug)] @@ -169,17 +89,14 @@ pub struct PtyBus { fn stream_terminal_bytes( pid: RawFd, - mut send_screen_instructions: SenderWithContext, + send_screen_instructions: SenderWithContext, os_input: Box, debug: bool, ) -> JoinHandle<()> { - let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); + let mut err_ctx = get_current_ctx(); task::spawn({ async move { err_ctx.add_call(ContextType::AsyncTask); - send_screen_instructions.update(err_ctx); - let mut vte_parser = vte::Parser::new(); - let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone()); let mut terminal_bytes = ReadFromPid::new(&pid, os_input); let mut last_byte_receive_time: Option = None; @@ -188,13 +105,13 @@ fn stream_terminal_bytes( while let Some(bytes) = terminal_bytes.next().await { let bytes_is_empty = bytes.is_empty(); - for byte in bytes { - if debug { - debug_to_file(byte, pid).unwrap(); + if debug { + for byte in bytes.iter() { + debug_to_file(*byte, pid).unwrap(); } - vte_parser.advance(&mut vte_event_sender, byte); } if !bytes_is_empty { + let _ = send_screen_instructions.send(ScreenInstruction::PtyBytes(pid, bytes)); // for UX reasons, if we got something on the wire, we only send the render notice if: // 1. there aren't any more bytes on the wire afterwards // 2. a certain period (currently 30ms) has elapsed since the last render diff --git a/src/common/screen.rs b/src/common/screen.rs index 121f21bd..01ac04fc 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -8,7 +8,7 @@ use std::sync::mpsc::Receiver; use super::{AppInstruction, SenderWithContext}; use crate::os_input_output::OsApi; use crate::panes::PositionAndSize; -use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::pty_bus::{PtyInstruction, VteBytes}; use crate::tab::Tab; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{layout::Layout, panes::PaneId}; @@ -18,7 +18,7 @@ use zellij_tile::data::{Event, ModeInfo, TabInfo}; /// Instructions that can be sent to the [`Screen`]. #[derive(Debug, Clone)] pub enum ScreenInstruction { - Pty(RawFd, VteEvent), + PtyBytes(RawFd, VteBytes), Render, NewPane(PaneId), HorizontalSplit(PaneId), @@ -28,7 +28,9 @@ pub enum ScreenInstruction { ResizeRight, ResizeDown, ResizeUp, - MoveFocus, + SwitchFocus, + FocusNextPane, + FocusPreviousPane, MoveFocusLeft, MoveFocusDown, MoveFocusUp, @@ -79,7 +81,9 @@ pub struct Screen { } impl Screen { + // FIXME: This lint needs actual fixing! Maybe by bundling the Senders /// Creates and returns a new [`Screen`]. + #[allow(clippy::too_many_arguments)] pub fn new( receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>, send_pty_instructions: SenderWithContext, diff --git a/src/common/utils/logging.rs b/src/common/utils/logging.rs index d08639aa..06827bfc 100644 --- a/src/common/utils/logging.rs +++ b/src/common/utils/logging.rs @@ -47,8 +47,7 @@ pub fn _debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> { } } -#[allow(dead_code)] -pub fn delete_log_file() -> io::Result<()> { +pub fn _delete_log_file() -> io::Result<()> { if fs::metadata(ZELLIJ_TMP_LOG_FILE).is_ok() { fs::remove_file(ZELLIJ_TMP_LOG_FILE) } else { @@ -56,8 +55,7 @@ pub fn delete_log_file() -> io::Result<()> { } } -#[allow(dead_code)] -pub fn delete_log_dir() -> io::Result<()> { +pub fn _delete_log_dir() -> io::Result<()> { if fs::metadata(ZELLIJ_TMP_LOG_DIR).is_ok() { fs::remove_dir_all(ZELLIJ_TMP_LOG_DIR) } else { diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs index 93892d4f..d5ba943b 100644 --- a/src/common/utils/shared.rs +++ b/src/common/utils/shared.rs @@ -18,9 +18,9 @@ pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String { if actual_len > columns { let mut line = String::from(l); line.truncate(columns); - return line; + line } else { - return [l, &str::repeat(" ", columns - ansi_len(l))].concat(); + [l, &str::repeat(" ", columns - ansi_len(l))].concat() } }) .chain(iter::repeat(str::repeat(" ", columns))) diff --git a/src/main.rs b/src/main.rs index 716acb91..ec249e8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,62 +1,27 @@ -#[cfg(test)] -mod tests; - mod cli; mod common; +#[cfg(test)] +mod tests; // TODO mod server; mod client; +use crate::cli::CliArgs; +use crate::command_is_executing::CommandIsExecuting; +use crate::os_input_output::get_os_input; +use crate::utils::{ + consts::{ZELLIJ_IPC_PIPE, ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, + logging::*, +}; use client::{boundaries, layout, panes, tab}; use common::{ command_is_executing, errors, os_input_output, pty_bus, screen, start, utils, wasm_vm, ApiCommand, }; -use directories_next::ProjectDirs; - +use std::io::Write; use std::os::unix::net::UnixStream; -use std::{fs, io::Write}; - use structopt::StructOpt; -use crate::cli::CliArgs; -use crate::command_is_executing::CommandIsExecuting; -use crate::os_input_output::get_os_input; -use crate::pty_bus::VteEvent; -use crate::utils::{ - consts::{ZELLIJ_IPC_PIPE, ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, - logging::*, -}; - pub fn main() { - // First run installation of default plugins & layouts - let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); - let data_dir = project_dirs.data_dir(); - let mut assets = asset_map! { - "assets/layouts/default.yaml" => "layouts/default.yaml", - "assets/layouts/strider.yaml" => "layouts/strider.yaml", - }; - // FIXME: This is a hideous hack and I hate it (a lot) - #[cfg(not(feature = "publish"))] - assets.extend(asset_map! { - "target/status-bar.wasm" => "plugins/status-bar.wasm", - "target/tab-bar.wasm" => "plugins/tab-bar.wasm", - "target/strider.wasm" => "plugins/strider.wasm", - }); - #[cfg(feature = "publish")] - assets.extend(asset_map! { - "assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm", - "assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm", - "assets/plugins/strider.wasm" => "plugins/strider.wasm", - }); - - for (path, bytes) in assets { - let path = data_dir.join(path); - fs::create_dir_all(path.parent().unwrap()).unwrap(); - if !path.exists() { - fs::write(path, bytes).expect("Failed to install default assets!"); - } - } - let opts = CliArgs::from_args(); if let Some(split_dir) = opts.split { match split_dir { diff --git a/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml b/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml deleted file mode 100644 index 9227c080..00000000 --- a/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -direction: Horizontal -parts: - - direction: Vertical - split_size: - Percent: 40 - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 40 - - direction: Horizontal - expansion_boundary: true - - direction: Horizontal - split_size: - Percent: 40 - - direction: Vertical - split_size: - Percent: 40 diff --git a/src/tests/integration/expansion_boundary.rs b/src/tests/integration/expansion_boundary.rs deleted file mode 100644 index c68c1d5a..00000000 --- a/src/tests/integration/expansion_boundary.rs +++ /dev/null @@ -1,117 +0,0 @@ -use insta::assert_snapshot; -use std::path::PathBuf; - -use crate::panes::PositionAndSize; -use crate::tests::fakes::FakeInputOutput; -use crate::tests::utils::commands::{ - PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_MODE, SLEEP, SPAWN_TERMINAL_IN_PANE_MODE, - TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE, -}; -use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots}; -use crate::{start, CliArgs}; - -fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { - FakeInputOutput::new(fake_win_size.clone()) -} - -#[test] -pub fn new_panes_are_open_inside_expansion_border() { - let fake_win_size = PositionAndSize { - columns: 121, - rows: 50, - x: 0, - y: 0, - }; - let mut fake_input_output = get_fake_os_input(&fake_win_size); - fake_input_output.add_terminal_input(&[ - &PANE_MODE, - &SPAWN_TERMINAL_IN_PANE_MODE, - &SLEEP, - &QUIT, - ]); - let mut opts = CliArgs::default(); - opts.layout = Some(PathBuf::from( - "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml", - )); - - start(Box::new(fake_input_output.clone()), opts); - let output_frames = fake_input_output - .stdout_writer - .output_frames - .lock() - .unwrap(); - let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); - - let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap(); - assert_snapshot!(next_to_last_snapshot); -} - -#[test] -pub fn resize_pane_inside_expansion_border() { - let fake_win_size = PositionAndSize { - columns: 121, - rows: 50, - x: 0, - y: 0, - }; - let mut fake_input_output = get_fake_os_input(&fake_win_size); - fake_input_output.add_terminal_input(&[ - &PANE_MODE, - &SPAWN_TERMINAL_IN_PANE_MODE, - &RESIZE_MODE, - &RESIZE_DOWN_IN_RESIZE_MODE, - &SLEEP, - &QUIT, - ]); - let mut opts = CliArgs::default(); - opts.layout = Some(PathBuf::from( - "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml", - )); - - start(Box::new(fake_input_output.clone()), opts); - let output_frames = fake_input_output - .stdout_writer - .output_frames - .lock() - .unwrap(); - let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); - - let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap(); - assert_snapshot!(next_to_last_snapshot); -} - -#[test] -pub fn toggling_fullcsreen_in_expansion_border_expands_only_until_border() { - let fake_win_size = PositionAndSize { - columns: 121, - rows: 50, - x: 0, - y: 0, - }; - let mut fake_input_output = get_fake_os_input(&fake_win_size); - fake_input_output.add_terminal_input(&[ - &PANE_MODE, - &SPAWN_TERMINAL_IN_PANE_MODE, - &TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE, - &SLEEP, - &QUIT, - ]); - let mut opts = CliArgs::default(); - opts.layout = Some(PathBuf::from( - "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml", - )); - - start(Box::new(fake_input_output.clone()), opts); - let output_frames = fake_input_output - .stdout_writer - .output_frames - .lock() - .unwrap(); - let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); - - let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap(); - assert_snapshot!(next_to_last_snapshot); -} - -// TODO: -// * fullscreen with expansion boundary diff --git a/src/tests/integration/mod.rs b/src/tests/integration/mod.rs index 83cb7d22..0511fc9c 100644 --- a/src/tests/integration/mod.rs +++ b/src/tests/integration/mod.rs @@ -1,7 +1,6 @@ pub mod basic; pub mod close_pane; pub mod compatibility; -pub mod expansion_boundary; pub mod layouts; pub mod move_focus_down; pub mod move_focus_left; diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap deleted file mode 100644 index 661eaf96..00000000 --- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap +++ /dev/null @@ -1,55 +0,0 @@ ---- -source: src/tests/integration/expansion_boundary.rs -expression: next_to_last_snapshot - ---- -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ -───────────────────────────────────────────────┬─────────────────────────┬─────────────────────────────────────────────── -line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│prompt $ │line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb├─────────────────────────┤line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -prompt $ │prompt $ █ │prompt $ -───────────────────────────────────────────────┴─────────────────────────┴─────────────────────────────────────────────── -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap deleted file mode 100644 index a3d009ce..00000000 --- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap +++ /dev/null @@ -1,55 +0,0 @@ ---- -source: src/tests/integration/expansion_boundary.rs -expression: next_to_last_snapshot - ---- -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ -───────────────────────────────────────────────┬─────────────────────────┬─────────────────────────────────────────────── -line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│prompt $ │line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb├─────────────────────────┤line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -prompt $ │prompt $ █ │prompt $ -───────────────────────────────────────────────┴─────────────────────────┴─────────────────────────────────────────────── -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap deleted file mode 100644 index 41a54185..00000000 --- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap +++ /dev/null @@ -1,55 +0,0 @@ ---- -source: src/tests/integration/expansion_boundary.rs -expression: next_to_last_snapshot - ---- -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ -───────────────────────────────────────────────┬─────────────────────────┬─────────────────────────────────────────────── -line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -prompt $ │prompt $ █ │prompt $ -───────────────────────────────────────────────┴─────────────────────────┴─────────────────────────────────────────────── -line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prompt $ diff --git a/src/tests/utils.rs b/src/tests/utils.rs index 915adfb0..a58e8610 100644 --- a/src/tests/utils.rs +++ b/src/tests/utils.rs @@ -12,7 +12,7 @@ pub fn get_output_frame_snapshots( let mut snapshots = vec![]; for frame in output_frames.iter() { for byte in frame.iter() { - vte_parser.advance(&mut terminal_output, *byte); + vte_parser.advance(&mut terminal_output.grid, *byte); } let output_lines = terminal_output.read_buffer_as_lines(); let cursor_coordinates = terminal_output.cursor_coordinates(); diff --git a/zellij-tile/Cargo.toml b/zellij-tile/Cargo.toml index eb9a2d59..c9156833 100644 --- a/zellij-tile/Cargo.toml +++ b/zellij-tile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zellij-tile" -version = "0.5.0" +version = "0.6.0" authors = ["Brooks J Rady "] edition = "2018" description = "A small client-side library for writing Zellij plugins (tiles)"