mirror of
https://github.com/zellij-org/zellij.git
synced 2025-01-02 14:16:00 +03:00
Merge branch 'main' of https://github.com/zellij-org/zellij into unbind-default-keys
This commit is contained in:
commit
60ad749473
52
.github/workflows/rust.yml
vendored
52
.github/workflows/rust.yml
vendored
@ -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
|
||||
|
@ -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?
|
||||
|
||||
|
232
Cargo.lock
generated
232
Cargo.lock
generated
@ -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",
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -1,15 +1,12 @@
|
||||
[package]
|
||||
name = "zellij"
|
||||
version = "0.2.1"
|
||||
version = "0.3.3"
|
||||
authors = ["Aram Drevekenin <aram@poor.dev>"]
|
||||
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]
|
||||
|
110
Makefile.toml
Normal file
110
Makefile.toml
Normal file
@ -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"]
|
@ -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`.
|
||||
|
@ -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=(
|
||||
|
@ -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 <path> "
|
||||
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 <path> "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
|
@ -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
|
||||
|
@ -6,7 +6,6 @@ parts:
|
||||
Fixed: 1
|
||||
plugin: tab-bar
|
||||
- direction: Vertical
|
||||
expansion_boundary: true
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
|
@ -12,7 +12,6 @@ parts:
|
||||
Percent: 20
|
||||
plugin: strider
|
||||
- direction: Horizontal
|
||||
expansion_boundary: true
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 2
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
25
build-all.sh
25
build-all.sh
@ -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 $@
|
32
build.rs
32
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")));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
23
publish.sh
23
publish.sh
@ -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 $@
|
@ -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"]
|
||||
|
@ -20,6 +20,10 @@ pub struct CliArgs {
|
||||
#[structopt(long)]
|
||||
pub max_panes: Option<usize>,
|
||||
|
||||
/// Change where zellij looks for layouts and plugins
|
||||
#[structopt(long)]
|
||||
pub data_dir: Option<PathBuf>,
|
||||
|
||||
/// Path to a layout yaml file
|
||||
#[structopt(short, long)]
|
||||
pub layout: Option<PathBuf>,
|
||||
@ -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<PathBuf>,
|
||||
#[structopt(long)]
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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<SplitSize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plugin: Option<PathBuf>,
|
||||
#[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()));
|
||||
|
||||
|
@ -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<PaneId, Box<dyn Pane>>,
|
||||
@ -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<PaneId, Box<dyn Pane>>,
|
||||
right_of: &Box<dyn Pane>,
|
||||
right_of: &dyn Pane,
|
||||
increase_by: usize,
|
||||
) -> Option<PaneId> {
|
||||
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<PaneId, Box<dyn Pane>>,
|
||||
below: &Box<dyn Pane>,
|
||||
below: &dyn Pane,
|
||||
increase_by: usize,
|
||||
) -> Option<PaneId> {
|
||||
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<PaneId, Box<dyn Pane>>,
|
||||
below: &Box<dyn Pane>,
|
||||
below: &dyn Pane,
|
||||
reduce_by: usize,
|
||||
) -> Option<PaneId> {
|
||||
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<PaneId, Box<dyn Pane>>,
|
||||
right_of: &Box<dyn Pane>,
|
||||
right_of: &dyn Pane,
|
||||
reduce_by: usize,
|
||||
) -> Option<PaneId> {
|
||||
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();
|
||||
}
|
||||
|
@ -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<Row>) -> Vec<Row> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bottom_canonical_row_and_wraps(rows: &mut Vec<Row>) -> Vec<Row> {
|
||||
fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
|
||||
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<Row>) -> Vec<Row> {
|
||||
}
|
||||
|
||||
fn transfer_rows_down(
|
||||
source: &mut Vec<Row>,
|
||||
source: &mut VecDeque<Row>,
|
||||
destination: &mut Vec<Row>,
|
||||
count: usize,
|
||||
max_src_width: Option<usize>,
|
||||
@ -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<Row>,
|
||||
destination: &mut Vec<Row>,
|
||||
destination: &mut VecDeque<Row>,
|
||||
count: usize,
|
||||
max_src_width: Option<usize>,
|
||||
max_dst_width: Option<usize>,
|
||||
@ -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<Row>, value: Row) {
|
||||
if vec.len() >= SCROLL_BACK {
|
||||
vec.pop_front();
|
||||
}
|
||||
vec.push_back(value)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Grid {
|
||||
lines_above: Vec<Row>,
|
||||
lines_above: VecDeque<Row>,
|
||||
viewport: Vec<Row>,
|
||||
lines_below: Vec<Row>,
|
||||
alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, 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. "[D")
|
||||
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<TerminalCharacter>) {
|
||||
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<TerminalCharacter>) {
|
||||
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<Row> {
|
||||
let mut parts: Vec<Row> = vec![];
|
||||
|
@ -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)> {
|
||||
|
@ -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));
|
||||
|
@ -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<Winsize> for PositionAndSize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TerminalPane {
|
||||
pub grid: Grid,
|
||||
pub alternative_grid: Option<Grid>, // 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<PositionAndSize>,
|
||||
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "[D")
|
||||
pub max_height: Option<usize>,
|
||||
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<Vec<TerminalCharacter>> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PtyInstruction>,
|
||||
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||
pub send_app_instructions: SenderWithContext<AppInstruction>,
|
||||
expansion_boundary: Option<PositionAndSize>,
|
||||
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<u8>) -> Vec<u8>;
|
||||
|
||||
@ -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<u8>) {
|
||||
@ -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<dyn Pane>)> = 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<dyn Pane>)> = 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) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<ConfigCli>) -> 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<ConfigCli>) -> ConfigResult {
|
||||
Ok(Config::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ConfigError {
|
||||
|
@ -26,6 +26,7 @@ struct InputHandler {
|
||||
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
||||
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||
send_app_instructions: SenderWithContext<AppInstruction>,
|
||||
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<u8>, 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) => {
|
||||
|
@ -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)
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<T: Clone> {
|
||||
/// synchronously or asynchronously depending on the underlying [`SenderType`].
|
||||
#[derive(Clone)]
|
||||
pub struct SenderWithContext<T: Clone> {
|
||||
err_ctx: ErrorContext,
|
||||
sender: SenderType<T>,
|
||||
}
|
||||
|
||||
impl<T: Clone> SenderWithContext<T> {
|
||||
fn new(err_ctx: ErrorContext, sender: SenderType<T>) -> Self {
|
||||
Self { err_ctx, sender }
|
||||
fn new(sender: SenderType<T>) -> 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<T: Clone> Send for SenderWithContext<T> {}
|
||||
@ -109,6 +105,12 @@ thread_local!(
|
||||
static OPENCALLS: RefCell<ErrorContext> = 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<ErrorContext> = RefCell::default()
|
||||
}
|
||||
|
||||
/// Instructions related to the entire application.
|
||||
#[derive(Clone)]
|
||||
pub enum AppInstruction {
|
||||
@ -140,25 +142,23 @@ pub fn start(mut os_input: Box<dyn OsApi>, 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<PtyInstruction> =
|
||||
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<AppInstruction> =
|
||||
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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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<dyn OsApi>, 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())
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
|
||||
Put(u8), // byte
|
||||
Unhook,
|
||||
OscDispatch(Vec<Vec<u8>>, bool), // params, bell_terminated
|
||||
CsiDispatch(Vec<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
|
||||
EscDispatch(Vec<u8>, bool, u8), // intermediates, ignore, byte
|
||||
}
|
||||
|
||||
struct VteEventSender {
|
||||
id: RawFd,
|
||||
sender: SenderWithContext<ScreenInstruction>,
|
||||
}
|
||||
|
||||
impl VteEventSender {
|
||||
pub fn new(id: RawFd, sender: SenderWithContext<ScreenInstruction>) -> 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<u8>;
|
||||
|
||||
/// 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<ScreenInstruction>,
|
||||
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||
os_input: Box<dyn OsApi>,
|
||||
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<Instant> = 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
|
||||
|
@ -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<PtyInstruction>,
|
||||
|
@ -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 {
|
||||
|
@ -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)))
|
||||
|
55
src/main.rs
55
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 {
|
||||
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
@ -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 $
|
@ -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 $
|
@ -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 $
|
@ -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();
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zellij-tile"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Brooks J Rady <b.j.rady@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "A small client-side library for writing Zellij plugins (tiles)"
|
||||
|
Loading…
Reference in New Issue
Block a user