wip: merge main in

This commit is contained in:
denis 2021-04-22 11:45:29 +03:00
commit 8d98ca7da0
77 changed files with 1571 additions and 1452 deletions

View File

@ -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

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [Unreleased]
* Change config to flag (https://github.com/zellij-org/zellij/pull/300)
* Add ZELLIJ environment variable on startup (https://github.com/zellij-org/zellij/pull/305)
* Terminal fix: do not clear line if it's not there (https://github.com/zellij-org/zellij/pull/289)
* Do not allow opening new pane on the status bar (https://github.com/zellij-org/zellij/pull/314)
* Allow scrolling by full pages (https://github.com/zellij-org/zellij/pull/298)
* Reduce crate size by 4.8MB using `cargo diet`, to 77kB (https://github.com/zellij-org/zellij/pull/293)
## [0.5.0] - 2021-04-20
Beta release with all the things

View File

@ -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?

143
Cargo.lock generated
View File

@ -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"
@ -82,19 +84,19 @@ dependencies = [
[[package]]
name = "async-io"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd"
checksum = "fcb9af4888a70ad78ecb5efcb0ba95d66a3cf54a88b62ae81559954c7588c7a2"
dependencies = [
"concurrent-queue",
"fastrand",
"futures-lite",
"libc",
"log",
"nb-connect",
"once_cell",
"parking",
"polling",
"socket2",
"vec-arena",
"waker-fn",
"winapi",
@ -102,9 +104,9 @@ dependencies = [
[[package]]
name = "async-lock"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb"
checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
dependencies = [
"event-listener",
]
@ -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"
@ -413,9 +414,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",
@ -468,9 +469,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",
@ -478,9 +479,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",
@ -492,9 +493,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",
@ -605,9 +606,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",
@ -620,9 +621,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",
@ -630,15 +631,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",
@ -647,9 +648,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"
@ -668,9 +669,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",
@ -680,21 +681,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",
@ -942,9 +943,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libloading"
@ -1030,16 +1031,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238"
[[package]]
name = "nb-connect"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d"
dependencies = [
"libc",
"socket2",
]
[[package]]
name = "nix"
version = "0.19.1"
@ -1283,9 +1274,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
dependencies = [
"bitflags",
]
@ -1359,15 +1350,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"
@ -1577,9 +1559,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.68"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote",
@ -1691,15 +1673,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"
@ -1865,17 +1838,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
@ -2150,9 +2112,9 @@ checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf"
[[package]]
name = "wast"
version = "35.0.1"
version = "35.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5800e9f86a1eae935e38bea11e60fd253f6d514d153fb39b3e5535a7b37b56"
checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68"
dependencies = [
"leb128",
]
@ -2211,15 +2173,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"
@ -2264,7 +2217,7 @@ dependencies = [
[[package]]
name = "zellij"
version = "0.2.1"
version = "0.5.0"
dependencies = [
"ansi_term 0.12.1",
"async-std",
@ -2288,11 +2241,9 @@ dependencies = [
"strum",
"termion",
"termios",
"toml",
"unicode-truncate",
"unicode-width",
"vte 0.8.0",
"walkdir",
"wasmer",
"wasmer-wasi",
"xrdb",
@ -2301,7 +2252,7 @@ dependencies = [
[[package]]
name = "zellij-tile"
version = "0.5.0"
version = "1.0.0"
dependencies = [
"serde",
"serde_json",

View File

@ -1,14 +1,13 @@
[package]
name = "zellij"
version = "0.2.1"
version = "0.5.0"
authors = ["Aram Drevekenin <aram@poor.dev>"]
edition = "2018"
description = "Terminal workspace (WIP)"
description = "A terminal workspace with batteries included"
license = "MIT"
repository = "https://github.com/zellij-org/zellij"
[features]
publish = []
homepage = "https://zellij.dev"
include = ["src/**/*", "assets/plugins/*", "assets/layouts/*", "LICENSE.md", "README.md", "!**/*_test.*", "!**/tests/**/*"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -38,8 +37,8 @@ wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
interprocess = "1.0.1"
xrdb = "0.1.1"
colors-transform = "0.2.5"
zellij-tile = { path = "zellij-tile/", version = "0.5.0" }
colors-transform = "0.2.5
zellij-tile = { path = "zellij-tile/", version = "1.0.0" }
[dependencies.async-std]
version = "1.3.0"
@ -49,17 +48,15 @@ features = ["unstable"]
insta = "1.6.0"
[build-dependencies]
directories-next = "2.0"
structopt = "0.3"
walkdir = "2"
toml = "0.5.8"
[workspace]
members = [
"zellij-tile",
"default-tiles/status-bar",
"default-tiles/strider",
"default-tiles/tab-bar",
"default-plugins/status-bar",
"default-plugins/strider",
"default-plugins/tab-bar",
".",
]
[profile.release]

117
Makefile.toml Normal file
View File

@ -0,0 +1,117 @@
# 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_TARGET_DIR}
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_TARGET_DIR}/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-plugins-release", "wasm-opt-plugins", "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_TARGET_DIR}/release/${CARGO_MAKE_CRATE_NAME} ${CARGO_MAKE_TASK_ARGS}
end
'''
[tasks.build-plugins-release]
env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = ["."] }
run_task = { name = "build-release", fork = true }
[tasks.wasm-opt-plugins]
script_runner = "@duckscript"
script = '''
plugins = glob_array ${CARGO_TARGET_DIR}/wasm32-wasi/release/*.wasm
for plugin in ${plugins}
plugin_name = basename ${plugin}
plugin_out = set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/assets/plugins/${plugin_name}
if is_path_newer ${plugin} ${plugin_out}
exec wasm-opt -O ${plugin} -o ${plugin_out}
end
end
'''
# CI Releasing Zellij
[tasks.ci-build-release]
workspace = false
dependencies = ["build-plugins-release", "wasm-opt-plugins", "build-release"]
command = "cargo"
args = ["build", "--verbose", "--release", "--target", "${CARGO_MAKE_TASK_ARGS}"]
# Publishing Zellij
[tasks.publish]
clear = true
workspace = false
dependencies = ["build-plugins-release", "wasm-opt-plugins", "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"]

View File

@ -21,72 +21,38 @@
[Zellij](https://en.wikipedia.org/wiki/Zellij) is a workspace aimed at developers, ops-oriented people and anyone who loves the terminal.
At its core, it is a terminal multiplexer (similar to [tmux](https://github.com/tmux/tmux) and [screen](https://www.gnu.org/software/screen/)), but this is merely its infrastructure layer.
For more details, read about upcoming features in our [roadmap](#roadmap).
Zellij includes a [layout system](https://zellij.dev/documentation/layouts.html), and a [plugin system](https://zellij.dev/documentation/plugins.html) allowing one to create plugins in any language that compiles to WebAssembly.
Right now Zellij is in its early development stages and is not yet ready for everyday usage.
If you're interested, watch this space or better yet - get involved!
For more details about our future plans, read about upcoming features in our [roadmap](#roadmap).
Zellij was initially called "Mosaic".
## How to use it?
## How do I install it?
You can install it through `cargo`:
```
cargo install zellij
```
Or you can download a prebuilt binary from our [Releases](https://github.com/zellij-org/zellij/releases).
## How do I hack on 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)
For more build commands, take a look at [`Contributing.md`](CONTRIBUTING.md).
The status bar on the bottom should guide you through the possible keyboard shortcuts in the app.
## Configuration
For configuring Zellij, please see the [Configuration documentation](https://zellij.dev/documentation/configuration.html).
# 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`.
## What is the current status of the project?
Zellij will look for a file `/zellij/config.yaml` in the default configuration location of your os.
Zellij should be ready for everyday use, but it's still classified as a beta. This means that there might be a rare crash or wrong behaviour here and there, but that once found it should be fixed rather quickly. If this happens to you, we would be very happy if you could open an issue and tell us how to reproduce it as best you can.
To pass a config file directly to zellij run it either with:
`cargo run -- config [FILE]` or `zellij config [FILE]`.
## How do I get involved?
The structure is as follows:
```
keybinds:
normal:
- action: []
key: []
```
`normal` is one of the `modes` zellij can be in.
It is possible to bind a sequence of actions to numerous keys at the same time.
Here a reference to the [Key](https://docs.rs/termion/1.5.6/termion/event/enum.Key.html) format that is used.
For example:
```
keybinds:
normal:
- action: [ NewTab, GoToTab: 1,]
key: [ Char: 'c',]
```
Will create a new tab and then switch to tab number 1 on pressing the
`c` key.
Whereas:
```
keybinds:
normal:
- action: [ NewTab,]
key: [ Char: 'c', Char: 'd',]
```
Will create a new tab on pressing either the `c` or the `d` key.
# What is the current status of the project?
Zellij is in the last stages of being VT compatible. As much as modern terminals are.
Most things should work inside a terminal pane, but some edge cases don't. Fixing these edge cases is a priority, so please open an issue if you find a bug.
Zellij is in its alpha stage right now. Please treat it accordingly.
# How do I get involved?
At the moment, the project is in early development.
A lot of the work needed to be done is product work (making decisions about what Zellij will be and do) as well as development work. Most tasks would probably involve a little of both.
We're a small team of enthusiasts, and we eagerly welcome people who would like to join in at this early stage.
We welcome all contributors, regardless of experience level. We believe any person who would like to contribute can make the project better!
Zellij is a labour of love built by an enthusiastic team of volunteers. We eagerly welcome anyone who would like to join us, regardless of experience level.
To get started, you can:
1. Take a look at the "Issues" in this repository - especially those marked "Good first issue". Those with the "Help Wanted" tag probably don't have anyone else working on them.
@ -95,22 +61,18 @@ To get started, you can:
And most importantly, please read our [code of conduct](CODE_OF_CONDUCT.md).
# Roadmap
## Roadmap
This section contains an ever-changing list of the major features that are either currently being worked on, or planned for the near future.
* **A web client/server** - Connect to Zellij through the browser instead of opening a terminal window. Either on a local or remote machine.
* **Share sessions with others** - See the focused window and cursor of other users, work on a problem or a code base together in real time.
* **Support for multiple terminal windows across screens** - Transfer panes across different windows and screens by having them all belong to the same session.
* **Smart layouts** - expand the current layout system so that it rearranges and hides panes intelligently when new ones are added or the window size is changed.
* <b>A layout engine</b> that would allow you to define how your panes will be (re)arranged when you open or close them. As well as when you change the terminal window size.
* <b>Pane types beyond a simple shell prompt</b>, for example:
- A file explorer (similar to ranger) that opens files for editing in a new pane.
- A launcher that opens any command you enter in a new pane
- A command pane that would run any command, display its output and re-run that command when clicked. Changing its frame colour to green/yellow/red depending on the exit status.
* <b>A Webassembly plugin system for compiled languages</b> built using WASI to allow you to write plugins in any compiled language. These plugins would be able to create new panes, interact with existing ones, interact with the filesystem and subscribe to events. You could consume them at runtime and decide what permissions to give them.
* <b>Built in support for portable workspaces across machines, projects and teams</b>: imagine being able to include a configuration file with your project that would include all the layouts and plugins that would best help new developers getting onboarded. Including all the shortcuts, customized panes and help-message hints triggered by things such as opening a file, entering a folder or running a command. How about being able to log into a new server or container, start Zellij with a URL of a git repository including your favorite configuration and plugins, and working with it as if you were on your own machine?
* <b>Support for multiple terminal windows across screens</b>: Why limit yourself to one terminal window? Zellij would allow you to transfer panes, view powerlines, get alerts and control your workspace from different windows by having them all belong to the same session.
# Contributing
## Contributing
Take a look at [`Contributing.md`](CONTRIBUTING.md) guide.
# License
## License
MIT

View File

@ -20,8 +20,11 @@ _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]' \
'-c+[Change where zellij looks for the configuration]' \
'--config=[Change where zellij looks for the configuration]' \
'-m[Send "move focused pane" to active zellij session]' \
'--move-focus[Send "move focused pane" to active zellij session]' \
'-d[]' \
@ -39,34 +42,13 @@ _zellij() {
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:zellij-command-$line[1]:"
case $line[1] in
(c)
(option)
_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]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
'::path:_files' \
&& ret=0
;;
(help)
@ -85,32 +67,11 @@ esac
(( $+functions[_zellij_commands] )) ||
_zellij_commands() {
local commands; commands=(
"config:Path to the configuration yaml file" \
"option:Change the behaviour of zellij" \
"help:Prints this message or the help of the given subcommand(s)" \
)
_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=(
)
_describe -t commands 'zellij config commands' commands "$@"
}
(( $+functions[_zellij__help_commands] )) ||
_zellij__help_commands() {
local commands; commands=(
@ -118,5 +79,12 @@ _zellij__help_commands() {
)
_describe -t commands 'zellij help commands' commands "$@"
}
(( $+functions[_zellij__option_commands] )) ||
_zellij__option_commands() {
local commands; commands=(
)
_describe -t commands 'zellij option commands' commands "$@"
}
_zellij "$@"

View File

@ -13,15 +13,12 @@ _zellij() {
cmd="zellij"
;;
c)
cmd+="__c"
;;
config)
cmd+="__config"
;;
help)
cmd+="__help"
;;
option)
cmd+="__option"
;;
*)
;;
esac
@ -29,7 +26,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 c c"
opts=" -m -d -h -V -s -o -l -c --move-focus --debug --help --version --split --open-file --max-panes --data-dir --layout --config option help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -56,6 +53,10 @@ _zellij() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--data-dir)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--layout)
COMPREPLY=($(compgen -f "${cur}"))
return 0
@ -64,6 +65,14 @@ _zellij() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--config)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-c)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
@ -72,36 +81,6 @@ _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
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
zellij__help)
opts=" -h -V --help --version "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
@ -117,6 +96,21 @@ _zellij() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
zellij__option)
opts=" -h -V --clean --help --version "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
}

View File

@ -1,15 +1,17 @@
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 c -l config -d 'Change where zellij looks for the configuration'
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
complete -c zellij -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
complete -c zellij -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
complete -c zellij -n "__fish_use_subcommand" -f -a "config" -d 'Path to the configuration yaml file'
complete -c zellij -n "__fish_use_subcommand" -f -a "option" -d 'Change the behaviour of zellij'
complete -c zellij -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
complete -c zellij -n "__fish_seen_subcommand_from config" -l clean -d 'Disables loading of configuration file at default location'
complete -c zellij -n "__fish_seen_subcommand_from config" -s h -l help -d 'Prints help information'
complete -c zellij -n "__fish_seen_subcommand_from config" -s V -l version -d 'Prints version information'
complete -c zellij -n "__fish_seen_subcommand_from option" -l clean -d 'Disables loading of configuration file at default location'
complete -c zellij -n "__fish_seen_subcommand_from option" -s h -l help -d 'Prints help information'
complete -c zellij -n "__fish_seen_subcommand_from option" -s V -l version -d 'Prints version information'
complete -c zellij -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
complete -c zellij -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'

View File

@ -6,7 +6,6 @@ parts:
Fixed: 1
plugin: tab-bar
- direction: Vertical
expansion_boundary: true
- direction: Vertical
split_size:
Fixed: 2

View File

@ -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.

View File

@ -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 $@

View File

@ -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")));
}

View File

@ -27,7 +27,7 @@ struct State {
mode_info: ModeInfo,
}
register_tile!(State);
register_plugin!(State);
#[derive(Default)]
pub struct LinePart {
@ -41,7 +41,7 @@ impl Display for LinePart {
}
}
impl ZellijTile for State {
impl ZellijPlugin for State {
fn load(&mut self) {
set_selectable(false);
set_invisible_borders(true);

View File

@ -2,7 +2,7 @@
use ansi_term::{ANSIStrings, Color::RGB, Style};
use zellij_tile::{data::Theme, prelude::*};
use crate::colors::{BLACK, GREEN, ORANGE, WHITE};
use crate::colors::{GREEN, ORANGE, WHITE};
use crate::{LinePart, MORE_MSG};
fn full_length_shortcut(

View File

@ -3,7 +3,7 @@ name = "strider"
version = "0.2.0"
authors = ["Brooks J Rady <b.j.rady@gmail.com>"]
edition = "2018"
description = "A simplified ranger clone written as a Zellij tile"
description = "A simplified ranger clone written as a Zellij plugin"
license = "MIT"
[dependencies]

View File

@ -5,9 +5,9 @@ use state::{FsEntry, State};
use std::{cmp::min, fs::read_dir};
use zellij_tile::prelude::*;
register_tile!(State);
register_plugin!(State);
impl ZellijTile for State {
impl ZellijPlugin for State {
fn load(&mut self) {
refresh_directory(self);
subscribe(&[EventType::KeyPress]);

View File

@ -31,9 +31,9 @@ pub mod colors {
pub const RED: Colour = Fixed(88);
}
register_tile!(State);
register_plugin!(State);
impl ZellijTile for State {
impl ZellijPlugin for State {
fn load(&mut self) {
set_selectable(false);
set_invisible_borders(true);

5
example/README.md Normal file
View File

@ -0,0 +1,5 @@
# Special considerations when using the configuration:
While trying to bind the newline character in the Config, use double quotes:
`Ctrl: "\n"` instead of `Ctrl: '\n'`

215
example/default.yaml Normal file
View File

@ -0,0 +1,215 @@
---
keybinds:
unbind: true
normal:
- action: [SwitchToMode: Locked,]
key: [Ctrl: 'g',]
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p',]
- action: [SwitchToMode: Resize,]
key: [Ctrl: 'r',]
- action: [SwitchToMode: Tab,]
key: [Ctrl: 't',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's',]
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [NewPane: ]
key: [ Alt: 'n',]
- action: [MoveFocus: Left,]
key: [ Alt: 'h',]
- action: [MoveFocus: Right,]
key: [ Alt: 'l',]
- action: [MoveFocus: Down,]
key: [ Alt: 'j',]
- action: [MoveFocus: Up,]
key: [ Alt: 'k',]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
locked:
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'g',]
resize:
- action: [SwitchToMode: Locked,]
key: [Ctrl: 'g']
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p', ]
- action: [SwitchToMode: Tab,]
key: [Ctrl: 't', ]
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
- action: [Quit]
key: [Ctrl: 'q']
- action: [Resize: Left,]
key: [Char: 'h', Left, ]
- action: [Resize: Down,]
key: [Char: 'h', Down, ]
- action: [Resize: Up,]
key: [Char: 'h', Up, ]
- action: [Resize: Right,]
key: [Char: 'h', Right, ]
- action: [NewPane: ,]
key: [ Alt: 'n',]
- action: [MoveFocus: Left,]
key: [ Alt: 'h', Left,]
- action: [MoveFocus: Right,]
key: [ Alt: 'l', Right,]
- action: [MoveFocus: Down,]
key: [ Alt: 'j', Down,]
- action: [MoveFocus: Up,]
key: [ Alt: 'k', Up,]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
pane:
- action: [SwitchToMode: Locked,]
key: [Ctrl: 'g']
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p', ]
- action: [SwitchToMode: Tab,]
key: [Ctrl: 't', ]
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [MoveFocus: Left,]
key: [ Alt: 'h', Left,]
- action: [MoveFocus: Right,]
key: [ Alt: 'l', Right,]
- action: [MoveFocus: Down,]
key: [ Alt: 'j', Down,]
- action: [MoveFocus: Up,]
key: [ Alt: 'k', Up,]
- action: [SwitchFocus,]
key: [Char: 'p']
- action: [NewPane: ,]
key: [Char: 'n', Alt: 'n',]
- action: [NewPane: Down,]
key: [Char: 'd', ]
- action: [NewPane: Right,]
key: [Char: 'r', ]
- action: [CloseFocus,]
key: [Char: 'x', ]
- action: [ToggleFocusFullscreen,]
key: [Char: 'f', ]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
tab:
- action: [SwitchToMode: Locked,]
key: [Ctrl: 'g']
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p', ]
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Ctrl: 't', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
- action: [SwitchToMode: RenameTab, TabNameInput: [0],]
key: [Char: 'r']
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
- action: [GoToPreviousTab,]
key: [ Char: 'h',]
- action: [GoToNextTab,]
key: [ Char: 'l', ]
- action: [GoToNextTab,]
key: [ Char: 'j', ]
- action: [GoToPreviousTab,]
key: [ Char: 'k', ]
- action: [NewTab,]
key: [ Char: 'n', ]
- action: [CloseTab,]
key: [ Char: 'x', ]
- action: [MoveFocus: Left,]
key: [ Alt: 'h',]
- action: [MoveFocus: Right,]
key: [ Alt: 'l', ]
- action: [MoveFocus: Down,]
key: [ Alt: 'j', ]
- action: [MoveFocus: Up,]
key: [ Alt: 'k', ]
- action: [GoToTab: 1,]
key: [ Char: '1', ]
- action: [GoToTab: 2,]
key: [ Char: '2', ]
- action: [GoToTab: 3,]
key: [ Char: '3', ]
- action: [GoToTab: 4,]
key: [ Char: '4', ]
- action: [GoToTab: 5,]
key: [ Char: '5', ]
- action: [GoToTab: 6,]
key: [ Char: '6', ]
- action: [GoToTab: 7,]
key: [ Char: '7', ]
- action: [GoToTab: 8,]
key: [ Char: '8', ]
- action: [GoToTab: 9,]
key: [ Char: '9', ]
scroll:
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Ctrl: 's', Char: ' ',
Char: "\n",]
- action: [SwitchToMode: Tab,]
key: [Ctrl: 't', ]
- action: [SwitchToMode: Locked,]
key: [Ctrl: 'g', ]
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p', ]
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [ScrollDown,]
key: [Char: 'j', Down,]
- action: [ScrollUp,]
key: [Char: 'k', Up,]
- action: [PageScrollDown,]
key: [Ctrl: 'f', PageDown,]
- action: [PageScrollUp,]
key: [Ctrl: 'b', PageUp,]
- action: [NewPane: ,]
key: [ Alt: 'n',]
- action: [MoveFocus: Left,]
key: [ Alt: 'h',]
- action: [MoveFocus: Right,]
key: [ Alt: 'l',]
- action: [MoveFocus: Down,]
key: [ Alt: 'j',]
- action: [MoveFocus: Up,]
key: [ Alt: 'k',]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
renametab:
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Ctrl: 's', Char: ' ', Char: 'g',]
- action: [SwitchToMode: Tab,]
key: [Char: "\n",]
- action: [TabNameInput: [27] , SwitchToMode: Tab,]
key: [Esc,]
- action: [NewPane: ,]
key: [ Alt: 'n',]
- action: [MoveFocus: Left,]
key: [ Alt: 'h',]
- action: [MoveFocus: Right,]
key: [ Alt: 'l',]
- action: [MoveFocus: Down,]
key: [ Alt: 'j',]
- action: [MoveFocus: Up,]
key: [ Alt: 'k',]
- action: [FocusPreviousPane,]
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]

View File

@ -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 $@

View File

@ -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"]

View File

@ -1,6 +1,9 @@
use std::path::PathBuf;
use structopt::StructOpt;
// TODO add to consts.rs
const ZELLIJ_CONFIG_ENV: &str = "ZELLIJ_CONFIG";
#[derive(StructOpt, Default, Debug)]
#[structopt(name = "zellij")]
pub struct CliArgs {
@ -20,12 +23,20 @@ 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>,
/// Change where zellij looks for the configuration
#[structopt(short, long, env=ZELLIJ_CONFIG_ENV)]
pub config: Option<PathBuf>,
#[structopt(subcommand)]
pub config: Option<ConfigCli>,
pub option: Option<ConfigCli>,
#[structopt(short, long)]
pub debug: bool,
@ -33,10 +44,9 @@ pub struct CliArgs {
#[derive(Debug, StructOpt)]
pub enum ConfigCli {
/// Path to the configuration yaml file
#[structopt(alias = "c")]
/// Change the behaviour of zellij
#[structopt(name = "option")]
Config {
path: Option<PathBuf>,
#[structopt(long)]
/// Disables loading of configuration file at default location
clean: bool,

View File

@ -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()));

View File

@ -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(&current_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(&current_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, &current_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, &current_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, &current_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, &current_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();
}

View File

@ -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. "")
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) {
@ -546,15 +606,17 @@ impl Grid {
row.replace_beginning_with(line_part);
}
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
let cursor_row = self.viewport.get_mut(self.cursor.y).unwrap();
cursor_row.truncate(self.cursor.x);
let mut replace_with_columns_in_cursor_row = vec![replace_with; self.width - self.cursor.x];
cursor_row.append(&mut replace_with_columns_in_cursor_row);
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y).as_mut() {
cursor_row.truncate(self.cursor.x);
let mut replace_with_columns_in_cursor_row =
vec![replace_with; self.width - self.cursor.x];
cursor_row.append(&mut replace_with_columns_in_cursor_row);
let replace_with_columns = vec![replace_with; self.width];
self.replace_characters_in_line_after_cursor(replace_with);
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
row.replace_columns(replace_with_columns.clone());
let replace_with_columns = vec![replace_with; self.width];
self.replace_characters_in_line_after_cursor(replace_with);
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
row.replace_columns(replace_with_columns.clone());
}
}
}
pub fn clear_cursor_line(&mut self) {
@ -600,7 +662,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 +685,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 +727,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 +751,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 +793,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 +1170,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 +1202,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 +1214,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![];

View File

@ -1,5 +1,3 @@
#![allow(clippy::clippy::if_same_then_else)]
use crate::{common::SenderWithContext, pty_bus::VteBytes, tab::Pane, wasm_vm::PluginInstruction};
use std::{sync::mpsc::channel, unimplemented};

View File

@ -48,7 +48,7 @@ pub enum NamedColor {
}
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),
@ -68,7 +68,7 @@ impl NamedColor {
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),

View File

@ -1,5 +1,3 @@
#![allow(clippy::clippy::if_same_then_else)]
use crate::tab::Pane;
use ::nix::pty::Winsize;
use ::std::os::unix::io::RawFd;
@ -10,7 +8,6 @@ use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
};
use crate::pty_bus::VteBytes;
use crate::utils::logging::debug_log_to_file;
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
pub enum PaneId {
@ -38,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. "")
pub max_height: Option<usize>,
pending_styles: CharacterStyles,
clear_viewport_before_rendering: bool,
vte_parser: vte::Parser,
}
impl Pane for TerminalPane {
@ -86,9 +78,8 @@ impl Pane for TerminalPane {
self.reflow_lines();
}
fn handle_pty_bytes(&mut self, bytes: VteBytes) {
let mut vte_parser = vte::Parser::new();
for byte in bytes.iter() {
vte_parser.advance(self, *byte);
self.vte_parser.advance(&mut self.grid, *byte);
}
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
@ -103,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();
@ -111,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();
@ -119,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();
@ -127,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();
@ -142,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
@ -175,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();
@ -188,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();
@ -212,7 +203,7 @@ impl Pane for TerminalPane {
}
character_styles.clear();
}
self.should_render = false;
self.grid.should_render = false;
Some(vte_output)
} else {
None
@ -271,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,
@ -333,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()
}
@ -346,328 +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 reset_terminal_state(&mut self) {
let rows = self.get_rows();
let columns = self.get_columns();
self.grid = Grid::new(rows, columns);
self.alternative_grid = None;
self.cursor_key_mode = false;
}
fn add_newline(&mut self) {
let pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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 pad_character = EMPTY_TERMINAL_CHARACTER;
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) {
match (byte, intermediates.get(0)) {
(b'M', None) => {
self.grid.move_cursor_up_with_scrolling(1);
}
(b'c', None) => {
self.reset_terminal_state();
}
_ => {}
}
}
}

View File

@ -20,7 +20,10 @@ use std::{io::Write, sync::mpsc::channel};
use zellij_tile::data::{colors, Event, InputMode, ModeInfo, Palette};
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
const MIN_TERMINAL_HEIGHT: usize = 2;
// MIN_TERMINAL_HEIGHT here must be larger than the height of any of the status bars
// this is a dirty hack until we implement fixed panes
const MIN_TERMINAL_HEIGHT: usize = 3;
const MIN_TERMINAL_WIDTH: usize = 4;
type BorderAndPaneIds = (usize, Vec<PaneId>);
@ -67,7 +70,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,
pub input_mode: InputMode,
@ -208,6 +210,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,
@ -250,7 +253,6 @@ impl Tab {
send_app_instructions,
send_pty_instructions,
send_plugin_instructions,
expansion_boundary: None,
should_clear_display_before_rendering: false,
mode_info,
input_mode,
@ -293,9 +295,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
@ -365,9 +364,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));
@ -627,19 +624,17 @@ 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();
if self.panes_to_hide.is_empty() {
// nothing to do, pane is already as fullscreen as it can be, let's bail
@ -647,9 +642,9 @@ impl Tab {
} else {
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,
self.full_screen_ws.x,
self.full_screen_ws.y,
&self.full_screen_ws,
);
}
}
@ -1503,21 +1498,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,
@ -1533,17 +1513,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
@ -1564,17 +1538,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
@ -1595,17 +1563,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
@ -1622,17 +1584,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
@ -1649,11 +1605,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
@ -1670,11 +1624,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
@ -1691,11 +1643,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
@ -1712,11 +1662,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
@ -1724,27 +1672,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) {
@ -2242,6 +2181,30 @@ impl Tab {
self.render();
}
}
pub fn scroll_active_terminal_up_page(&mut self) {
if let Some(active_terminal_id) = self.get_active_terminal_id() {
let active_terminal = self
.panes
.get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap();
// prevent overflow when row == 0
let scroll_columns = active_terminal.rows().max(1) - 1;
active_terminal.scroll_up(scroll_columns);
self.render();
}
}
pub fn scroll_active_terminal_down_page(&mut self) {
if let Some(active_terminal_id) = self.get_active_terminal_id() {
let active_terminal = self
.panes
.get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap();
// prevent overflow when row == 0
let scroll_columns = active_terminal.rows().max(1) - 1;
active_terminal.scroll_down(scroll_columns);
self.render();
}
}
pub fn clear_active_terminal_scroll(&mut self) {
if let Some(active_terminal_id) = self.get_active_terminal_id() {
let active_terminal = self

View File

@ -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));
}
}
@ -188,6 +196,8 @@ pub enum ScreenContext {
Quit,
ScrollUp,
ScrollDown,
PageScrollUp,
PageScrollDown,
ClearScroll,
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
@ -230,6 +240,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::Quit => ScreenContext::Quit,
ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp,
ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown,
ScreenInstruction::ClearScroll => ScreenContext::ClearScroll,
ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane,
ScreenInstruction::ToggleActiveTerminalFullscreen => {

View File

@ -33,6 +33,10 @@ pub enum Action {
ScrollUp,
/// Scroll down in focus pane.
ScrollDown,
/// Scroll up one page in focus pane.
PageScrollUp,
/// Scroll down one page in focus pane.
PageScrollDown,
/// Toggle between fullscreen focus pane and normal layout.
ToggleFocusFullscreen,
/// Open a new pane in the specified direction (relative to focus).

View File

@ -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) => {
@ -80,20 +79,23 @@ impl Config {
/// Entry point of the configuration
#[cfg(not(test))]
pub fn from_cli_config(cli_config: Option<ConfigCli>) -> ConfigResult {
pub fn from_cli_config(
location: Option<PathBuf>,
cli_config: Option<ConfigCli>,
) -> ConfigResult {
if let Some(path) = location {
return Config::new(&path);
}
match cli_config {
Some(ConfigCli::Config { clean, .. }) if clean => Ok(Config::default()),
Some(ConfigCli::Config { path, .. }) if path.is_some() => {
Ok(Config::new(&path.unwrap())?)
}
Some(_) | None => Ok(Config::from_default_path()?),
}
}
//#[allow(unused_must_use)]
/// In order not to mess up tests from changing configurations
#[cfg(test)]
pub fn from_cli_config(_: Option<ConfigCli>) -> ConfigResult {
pub fn from_cli_config(_: Option<PathBuf>, _: Option<ConfigCli>) -> ConfigResult {
Ok(Config::default())
}
}
@ -139,19 +141,15 @@ mod config_test {
#[test]
fn clean_option_equals_default_config() {
let no_file = PathBuf::from(r"../fixtures/config/config.yamlll");
let cli_config = ConfigCli::Config {
path: Some(no_file),
clean: true,
};
let config = Config::from_cli_config(Some(cli_config)).unwrap();
let cli_config = ConfigCli::Config { clean: true };
let config = Config::from_cli_config(None, Some(cli_config)).unwrap();
let default = Config::default();
assert_eq!(config, default);
}
#[test]
fn no_config_option_file_equals_default_config() {
let config = Config::from_cli_config(None).unwrap();
let config = Config::from_cli_config(None, None).unwrap();
let default = Config::default();
assert_eq!(config, default);
}

View File

@ -59,9 +59,6 @@ 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();
let alt_left_bracket = vec![27, 91];
loop {
@ -188,6 +185,16 @@ impl InputHandler {
.send(ScreenInstruction::ScrollDown)
.unwrap();
}
Action::PageScrollUp => {
self.send_screen_instructions
.send(ScreenInstruction::PageScrollUp)
.unwrap();
}
Action::PageScrollDown => {
self.send_screen_instructions
.send(ScreenInstruction::PageScrollDown)
.unwrap();
}
Action::ToggleFocusFullscreen => {
self.send_screen_instructions
.send(ScreenInstruction::ToggleActiveTerminalFullscreen)
@ -298,6 +305,7 @@ pub fn get_mode_info(mode: InputMode) -> ModeInfo {
}
InputMode::Scroll => {
keybinds.push(("↓↑".to_string(), "Scroll".to_string()));
keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string()));
}
InputMode::RenameTab => {
keybinds.push(("Enter".to_string(), "when done".to_string()));

View File

@ -7,14 +7,30 @@ use serde::Deserialize;
use strum::IntoEnumIterator;
use zellij_tile::data::*;
/// Used in the config struct
#[derive(Clone, Debug, PartialEq)]
pub struct Keybinds(HashMap<InputMode, ModeKeybinds>);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ModeKeybinds(HashMap<Key, Vec<Action>>);
/// Intermediate struct used for deserialisation
/// Used in the config file.
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct KeybindsFromYaml(HashMap<InputMode, Vec<KeyActionFromYaml>>);
pub struct KeybindsFromYaml {
#[serde(flatten)]
keybinds: HashMap<InputMode, Vec<KeyActionUnbind>>,
#[serde(default)]
unbind: Unbind,
}
/// Intermediate enum used for deserialisation
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(untagged)]
enum KeyActionUnbind {
KeyAction(KeyActionFromYaml),
// TODO: use the enum
//Unbind(UnbindFromYaml),
}
/// Intermediate struct used for deserialisation
#[derive(Clone, Debug, PartialEq, Deserialize)]
@ -23,6 +39,22 @@ pub struct KeyActionFromYaml {
key: Vec<Key>,
}
/// Intermediate struct used for deserialisation
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
struct UnbindFromYaml {
unbind: Unbind,
}
/// List of keys, for which to disable their respective default actions
/// `All` is a catch all, and will disable the default actions for all keys.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
#[serde(untagged)]
enum Unbind {
All(bool),
// TODO@a-kenji: use the enum
//Keys(Vec<Key>),
}
impl Default for Keybinds {
fn default() -> Keybinds {
let mut defaults = Keybinds::new();
@ -41,9 +73,16 @@ impl Keybinds {
Keybinds(HashMap::<InputMode, ModeKeybinds>::new())
}
pub fn get_default_keybinds_with_config(keybinds: Option<KeybindsFromYaml>) -> Keybinds {
let default_keybinds = Keybinds::default();
if let Some(keybinds) = keybinds {
pub fn get_default_keybinds_with_config(from_yaml: Option<KeybindsFromYaml>) -> Keybinds {
let default_keybinds = match from_yaml.clone() {
Some(keybinds) => match keybinds.unbind {
Unbind::All(true) => Keybinds::new(),
Unbind::All(false) => Keybinds::default(),
},
None => Keybinds::default(),
};
if let Some(keybinds) = from_yaml {
default_keybinds.merge_keybinds(Keybinds::from(keybinds))
} else {
default_keybinds
@ -301,6 +340,11 @@ impl Keybinds {
defaults.insert(Key::Char('j'), vec![Action::ScrollDown]);
defaults.insert(Key::Char('k'), vec![Action::ScrollUp]);
defaults.insert(Key::Ctrl('f'), vec![Action::PageScrollDown]);
defaults.insert(Key::Ctrl('b'), vec![Action::PageScrollUp]);
defaults.insert(Key::PageDown, vec![Action::PageScrollDown]);
defaults.insert(Key::PageUp, vec![Action::PageScrollUp]);
defaults.insert(Key::Down, vec![Action::ScrollDown]);
defaults.insert(Key::Up, vec![Action::ScrollUp]);
@ -383,7 +427,7 @@ impl From<KeybindsFromYaml> for Keybinds {
for mode in InputMode::iter() {
let mut mode_keybinds = ModeKeybinds::new();
for key_action in keybinds_from_yaml.0.get(&mode).iter() {
for key_action in keybinds_from_yaml.keybinds.get(&mode).iter() {
for keybind in key_action.iter() {
mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone()));
}
@ -409,6 +453,21 @@ impl From<KeyActionFromYaml> for ModeKeybinds {
}
}
// Currently an enum for future use
impl From<KeyActionUnbind> for ModeKeybinds {
fn from(key_action_unbind: KeyActionUnbind) -> ModeKeybinds {
match key_action_unbind {
KeyActionUnbind::KeyAction(key_action) => ModeKeybinds::from(key_action),
}
}
}
impl Default for Unbind {
fn default() -> Unbind {
Unbind::All(false)
}
}
// The unit test location.
#[cfg(test)]
#[path = "./unit/keybinds_test.rs"]

View File

@ -124,12 +124,25 @@ fn from_keyaction_from_yaml_to_mode_keybindings() {
assert_eq!(expected, ModeKeybinds::from(keyaction));
}
//#[test]
//fn from_keybinds_from_yaml_to_keybinds(){
//let mut keybinds_from_yaml = KeybindsFromYaml(HashMap<InputMode, Vec<KeyActionFromYaml>>);
//let actions = vec![Action::NoOp, Action::GoToTab(1), ];
//let keyaction = KeyActionFromYaml {
//action : actions.clone(),
//key : vec![ Key::F(1), Key::Backspace , Key::Char('t'), ],
//};
//}
#[test]
fn toplevel_unbind_unbinds_all() {
let from_yaml = KeybindsFromYaml {
unbind: Unbind::All(true),
keybinds: HashMap::new(),
};
let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
assert_eq!(keybinds_from_yaml, Keybinds::new());
}
fn no_unbind_unbinds_none() {
let from_yaml = KeybindsFromYaml {
unbind: Unbind::All(false),
keybinds: HashMap::new(),
};
let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
assert_eq!(keybinds_from_yaml, Keybinds::new());
}

View File

@ -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!");
}
}
}

View File

@ -16,6 +16,7 @@ use std::thread;
use std::{collections::HashMap, fs};
use std::{
collections::HashSet,
env,
io::Write,
str::FromStr,
sync::{Arc, Mutex},
@ -26,10 +27,15 @@ use crate::common::input::config::Config;
use crate::layout::Layout;
use crate::panes::PaneId;
use colors_transform::{Color, Rgb};
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};
@ -76,32 +82,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> {}
@ -113,6 +110,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 {
@ -130,7 +133,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.write(take_snapshot.as_bytes())
.unwrap();
let config = Config::from_cli_config(opts.config)
env::set_var(&"ZELLIJ", "0");
let config = Config::from_cli_config(opts.config, opts.option)
.map_err(|e| {
eprintln!("There was an error in the config file:\n{}", e);
std::process::exit(1);
@ -144,25 +149,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,
@ -172,12 +175,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({
@ -199,7 +212,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);
@ -282,8 +294,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::Screen(ScreenContext::from(&event)));
screen.send_app_instructions.update(err_ctx);
screen.send_pty_instructions.update(err_ctx);
match event {
ScreenInstruction::PtyBytes(pid, vte_bytes) => {
let active_tab = screen.get_active_tab_mut().unwrap();
@ -369,6 +379,18 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.unwrap()
.scroll_active_terminal_down();
}
ScreenInstruction::PageScrollUp => {
screen
.get_active_tab_mut()
.unwrap()
.scroll_active_terminal_up_page();
}
ScreenInstruction::PageScrollDown => {
screen
.get_active_tab_mut()
.unwrap()
.scroll_active_terminal_down_page();
}
ScreenInstruction::ClearScroll => {
screen
.get_active_tab_mut()
@ -443,9 +465,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;
@ -455,14 +477,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")))
@ -566,16 +583,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 {
@ -647,8 +662,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;
@ -662,7 +675,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())

View File

@ -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;
}
}
}

View File

@ -8,11 +8,11 @@ use ::std::sync::mpsc::Receiver;
use ::std::time::{Duration, Instant};
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};
@ -43,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)
}
}
@ -89,15 +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 terminal_bytes = ReadFromPid::new(&pid, os_input);
let mut last_byte_receive_time: Option<Instant> = None;

View File

@ -38,6 +38,8 @@ pub enum ScreenInstruction {
Quit,
ScrollUp,
ScrollDown,
PageScrollUp,
PageScrollDown,
ClearScroll,
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
@ -83,7 +85,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>,

View File

@ -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 {

View File

@ -22,9 +22,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)))

View File

@ -1,23 +1,10 @@
#[cfg(test)]
mod tests;
mod cli;
mod common;
#[cfg(test)]
mod tests;
// TODO mod server;
mod client;
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::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;
@ -25,37 +12,16 @@ 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 std::io::Write;
use std::os::unix::net::UnixStream;
use structopt::StructOpt;
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 {

View File

@ -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

View File

@ -3,8 +3,9 @@ use ::insta::assert_snapshot;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::utils::commands::{
PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_UP_IN_SCROLL_MODE,
SPAWN_TERMINAL_IN_PANE_MODE, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
SCROLL_PAGE_UP_IN_SCROLL_MODE, SCROLL_UP_IN_SCROLL_MODE, SPAWN_TERMINAL_IN_PANE_MODE,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
@ -237,6 +238,67 @@ pub fn scrolling_down_inside_a_pane() {
assert_snapshot!(snapshot_before_quit);
}
#[test]
pub fn scrolling_page_up_inside_a_pane() {
let fake_win_size = PositionAndSize {
columns: 121,
rows: 20,
x: 0,
y: 0,
};
let mut fake_input_output = get_fake_os_input(&fake_win_size);
fake_input_output.add_terminal_input(&[
&PANE_MODE,
&SPLIT_DOWN_IN_PANE_MODE,
&SPLIT_RIGHT_IN_PANE_MODE,
&SCROLL_MODE,
&SCROLL_PAGE_UP_IN_SCROLL_MODE,
&QUIT,
]);
start(Box::new(fake_input_output.clone()), CliArgs::default());
let output_frames = fake_input_output
.stdout_writer
.output_frames
.lock()
.unwrap();
let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
let snapshot_before_quit =
get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
assert_snapshot!(snapshot_before_quit);
}
#[test]
pub fn scrolling_page_down_inside_a_pane() {
let fake_win_size = PositionAndSize {
columns: 121,
rows: 20,
x: 0,
y: 0,
};
let mut fake_input_output = get_fake_os_input(&fake_win_size);
fake_input_output.add_terminal_input(&[
&PANE_MODE,
&SPLIT_DOWN_IN_PANE_MODE,
&SPLIT_RIGHT_IN_PANE_MODE,
&SCROLL_MODE,
&SCROLL_PAGE_UP_IN_SCROLL_MODE,
&SCROLL_PAGE_UP_IN_SCROLL_MODE,
&SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
&SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
&QUIT,
]);
start(Box::new(fake_input_output.clone()), CliArgs::default());
let output_frames = fake_input_output
.stdout_writer
.output_frames
.lock()
.unwrap();
let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
let snapshot_before_quit =
get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
assert_snapshot!(snapshot_before_quit);
}
#[test]
pub fn max_panes() {
// with the --max-panes option, we only allow a certain amount of panes on screen

View File

@ -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

View File

@ -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;

View File

@ -98,17 +98,18 @@ pub fn resize_down_with_panes_above_and_below() {
// ┌───────────┐ ┌───────────┐
// │ │ │ │
// │ │ │ │
// ├───────────┤ │ │
// │███████████│ ==resize=down==> ├───────────┤
// │███████████│ │███████████│
// ├───────────┤ ├───────────┤
// │ │ │ │
// │███████████│ ==resize=down==> │███████████│
// │███████████│ │███████████│
// │███████████│ │███████████│
// ├───────────┤ │███████████│
// │ │ ├───────────┤
// │ │ │ │
// └───────────┘ └───────────┘
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
rows: 20,
rows: 25,
x: 0,
y: 0,
};
@ -599,7 +600,7 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() {
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
rows: 5,
rows: 7,
x: 0,
y: 0,
};

View File

@ -594,7 +594,7 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() {
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
rows: 5,
rows: 7,
x: 0,
y: 0,
};

View File

@ -0,0 +1,25 @@
---
source: src/tests/integration/basic.rs
expression: snapshot_before_quit
---
line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
prompt $ │prompt $ █

View File

@ -0,0 +1,25 @@
---
source: src/tests/integration/basic.rs
expression: snapshot_before_quit
---
line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line4-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line5-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line6-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a │line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
prompt $ │line12-bb█bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

View File

@ -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 $

View File

@ -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 $

View File

@ -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 $

View File

@ -3,8 +3,10 @@ source: src/tests/integration/resize_down.rs
expression: snapshot_before_quit
---
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █

View File

@ -3,6 +3,8 @@ source: src/tests/integration/resize_down.rs
expression: snapshot_before_quit
---
line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
@ -14,6 +16,8 @@ line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
@ -21,5 +25,6 @@ line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $

View File

@ -3,8 +3,10 @@ source: src/tests/integration/resize_up.rs
expression: snapshot_before_quit
---
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █

View File

@ -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();
@ -63,6 +63,8 @@ pub mod commands {
pub const SCROLL_MODE: [u8; 1] = [19]; // ctrl-s
pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [107]; // k
pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [106]; // j
pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = [2]; // ctrl-b
pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = [6]; // ctrl-f
pub const RESIZE_MODE: [u8; 1] = [18]; // ctrl-r
pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [106]; // j

View File

@ -1,9 +1,9 @@
[package]
name = "zellij-tile"
version = "0.5.0"
version = "1.0.0"
authors = ["Brooks J Rady <b.j.rady@gmail.com>"]
edition = "2018"
description = "A small client-side library for writing Zellij plugins (tiles)"
description = "A small client-side library for writing Zellij plugins"
license = "MIT"
[dependencies]

View File

@ -5,14 +5,14 @@ pub mod shim;
use data::*;
#[allow(unused_variables)]
pub trait ZellijTile {
pub trait ZellijPlugin {
fn load(&mut self) {}
fn update(&mut self, event: Event) {}
fn render(&mut self, rows: usize, cols: usize) {}
}
#[macro_export]
macro_rules! register_tile {
macro_rules! register_plugin {
($t:ty) => {
thread_local! {
static STATE: std::cell::RefCell<$t> = std::cell::RefCell::new(Default::default());