Unnamed repository; edit this file 'description' to name the repository.
Port to termwiz: compiles but no rendering yet
Blaž Hrastnik 2022-03-16
parent 20a132e · commit 7a51085
-rw-r--r--Cargo.lock415
-rw-r--r--helix-term/Cargo.toml3
-rw-r--r--helix-term/src/application.rs2
-rw-r--r--helix-term/src/compositor.rs76
-rw-r--r--helix-term/src/ui/completion.rs2
-rw-r--r--helix-term/src/ui/editor.rs13
-rw-r--r--helix-term/src/ui/info.rs2
-rw-r--r--helix-term/src/ui/menu.rs7
-rw-r--r--helix-term/src/ui/picker.rs7
-rw-r--r--helix-term/src/ui/popup.rs2
-rw-r--r--helix-term/src/ui/prompt.rs2
-rw-r--r--helix-tui/Cargo.toml4
-rw-r--r--helix-tui/src/backend/crossterm.rs197
-rw-r--r--helix-tui/src/backend/mod.rs26
-rw-r--r--helix-tui/src/backend/test.rs150
-rw-r--r--helix-tui/src/buffer.rs944
-rw-r--r--helix-tui/src/lib.rs4
-rw-r--r--helix-tui/src/terminal.rs225
-rw-r--r--helix-tui/src/widgets/block.rs108
-rw-r--r--helix-tui/src/widgets/paragraph.rs22
-rw-r--r--helix-tui/src/widgets/table.rs28
-rw-r--r--helix-view/Cargo.toml5
-rw-r--r--helix-view/src/graphics.rs48
23 files changed, 700 insertions, 1592 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 80645e1e..b4b4e29c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -30,12 +30,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -72,6 +87,12 @@ checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
@@ -82,7 +103,7 @@ version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"encoding_rs",
"memchr",
]
@@ -120,12 +141,21 @@ dependencies = [
]
[[package]]
+name = "cpufeatures"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "crossbeam-utils"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"lazy_static",
]
@@ -141,7 +171,7 @@ dependencies = [
"libc",
"mio 0.7.14",
"parking_lot",
- "signal-hook",
+ "signal-hook 0.3.13",
"signal-hook-mio",
"winapi",
]
@@ -156,16 +186,46 @@ dependencies = [
]
[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "dirs"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
+dependencies = [
+ "cfg-if 0.1.10",
+ "dirs-sys",
+]
+
+[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
+name = "dirs-sys"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -188,7 +248,7 @@ version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
]
[[package]]
@@ -216,7 +276,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016b04fd1e94fb833d432634245c9bb61cf1c7409668a0e7d4c3ab00c5172dec"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"dirs-next",
"thiserror",
]
@@ -231,6 +291,17 @@ dependencies = [
]
[[package]]
+name = "filedescriptor"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e"
+dependencies = [
+ "libc",
+ "thiserror",
+ "winapi",
+]
+
+[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -292,12 +363,33 @@ dependencies = [
]
[[package]]
+name = "generic-array"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
]
@@ -457,8 +549,9 @@ dependencies = [
"retain_mut",
"serde",
"serde_json",
- "signal-hook",
+ "signal-hook 0.3.13",
"signal-hook-tokio",
+ "termwiz",
"tokio",
"tokio-stream",
"toml",
@@ -475,6 +568,7 @@ dependencies = [
"helix-core",
"helix-view",
"serde",
+ "termwiz",
"unicode-segmentation",
]
@@ -496,6 +590,7 @@ dependencies = [
"once_cell",
"serde",
"slotmap",
+ "termwiz",
"tokio",
"tokio-stream",
"toml",
@@ -513,6 +608,12 @@ dependencies = [
]
[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -578,7 +679,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"winapi",
]
@@ -597,7 +698,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
]
[[package]]
@@ -635,6 +736,12 @@ dependencies = [
]
[[package]]
+name = "memmem"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
+
+[[package]]
name = "mio"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -671,6 +778,16 @@ dependencies = [
]
[[package]]
+name = "nom"
+version = "5.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
+dependencies = [
+ "memchr",
+ "version_check",
+]
+
+[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -680,6 +797,17 @@ dependencies = [
]
[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -715,6 +843,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -730,7 +873,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
@@ -744,6 +887,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -756,6 +946,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -781,7 +977,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
- "rand",
+ "rand 0.8.5",
]
[[package]]
@@ -795,11 +991,44 @@ dependencies = [
[[package]]
name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
- "rand_core",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
]
[[package]]
@@ -808,7 +1037,25 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
- "getrandom",
+ "getrandom 0.2.5",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
]
[[package]]
@@ -826,7 +1073,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
- "getrandom",
+ "getrandom 0.2.5",
"redox_syscall",
]
@@ -890,6 +1137,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
+name = "semver"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+dependencies = [
+ "pest",
+]
+
+[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -932,6 +1197,29 @@ dependencies = [
]
[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer",
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
name = "signal-hook"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -949,7 +1237,7 @@ checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
dependencies = [
"libc",
"mio 0.7.14",
- "signal-hook",
+ "signal-hook 0.3.13",
]
[[package]]
@@ -969,7 +1257,7 @@ checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e"
dependencies = [
"futures-core",
"libc",
- "signal-hook",
+ "signal-hook 0.3.13",
"tokio",
]
@@ -980,6 +1268,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
[[package]]
+name = "siphasher"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+
+[[package]]
name = "slab"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1043,6 +1337,60 @@ dependencies = [
]
[[package]]
+name = "terminfo"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e"
+dependencies = [
+ "dirs",
+ "fnv",
+ "nom",
+ "phf",
+ "phf_codegen",
+]
+
+[[package]]
+name = "termios"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "termwiz"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ef6892cc0348a9b3b8c377addba91e0f6365863d92354bf27559dca81ee8c5"
+dependencies = [
+ "anyhow",
+ "base64",
+ "bitflags",
+ "cfg-if 1.0.0",
+ "filedescriptor",
+ "hex",
+ "lazy_static",
+ "libc",
+ "log",
+ "memmem",
+ "num-derive",
+ "num-traits",
+ "ordered-float",
+ "regex",
+ "semver",
+ "sha2",
+ "signal-hook 0.1.17",
+ "terminfo",
+ "termios",
+ "thiserror",
+ "ucd-trie",
+ "unicode-segmentation",
+ "vtparse",
+ "winapi",
+]
+
+[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1157,6 +1505,18 @@ dependencies = [
]
[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1218,12 +1578,27 @@ dependencies = [
]
[[package]]
+name = "utf8parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
+name = "vtparse"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f41c9314c4dde1f43dd0c46c67bb5ae73850ce11eebaf7d8b912e178bda5401"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1236,6 +1611,12 @@ dependencies = [
[[package]]
name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml
index 48365743..fd939df7 100644
--- a/helix-term/Cargo.toml
+++ b/helix-term/Cargo.toml
@@ -35,8 +35,9 @@ which = "4.2"
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
num_cpus = "1"
-tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
+tui = { path = "../helix-tui", package = "helix-tui" }
crossterm = { version = "0.23", features = ["event-stream"] }
+termwiz = "0.15"
signal-hook = "0.3"
tokio-stream = "0.1"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 269ce13d..df7ce871 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -254,7 +254,7 @@ impl Application {
use helix_view::graphics::Rect;
match signal {
signal::SIGTSTP => {
- self.compositor.save_cursor();
+ self.compositor.restore_cursor();
self.restore_term().unwrap();
low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
}
diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs
index 4f988ace..ce29eb49 100644
--- a/helix-term/src/compositor.rs
+++ b/helix-term/src/compositor.rs
@@ -71,51 +71,52 @@ pub trait Component: Any + AnyComponent {
}
}
-use anyhow::Error;
-use std::io::stdout;
-use tui::backend::{Backend, CrosstermBackend};
-type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
+use termwiz::{
+ caps::Capabilities, surface::CursorVisibility, terminal::buffered::BufferedTerminal,
+ terminal::SystemTerminal,
+};
+type Terminal = BufferedTerminal<SystemTerminal>;
pub struct Compositor {
layers: Vec<Box<dyn Component>>,
terminal: Terminal,
+ surface: Surface,
pub(crate) last_picker: Option<Box<dyn Component>>,
}
impl Compositor {
- pub fn new() -> Result<Self, Error> {
- let backend = CrosstermBackend::new(stdout());
- let terminal = Terminal::new(backend)?;
+ pub fn new() -> Result<Self, termwiz::Error> {
+ let terminal = BufferedTerminal::new(SystemTerminal::new(Capabilities::new_from_env()?)?)?;
+ let (width, height) = terminal.dimensions();
+ let surface = Surface::new(width, height);
Ok(Self {
layers: Vec::new(),
terminal,
+ surface,
last_picker: None,
})
}
pub fn size(&self) -> Rect {
- self.terminal.size().expect("couldn't get terminal size")
+ let (width, height) = self.terminal.dimensions();
+ Rect::new(0, 0, width as u16, height as u16)
}
+ // TODO: pass in usize
pub fn resize(&mut self, width: u16, height: u16) {
- self.terminal
- .resize(Rect::new(0, 0, width, height))
- .expect("Unable to resize terminal")
+ self.terminal.resize(width as usize, height as usize)
}
- pub fn save_cursor(&mut self) {
- if self.terminal.cursor_kind() == CursorKind::Hidden {
- self.terminal
- .backend_mut()
- .show_cursor(CursorKind::Block)
- .ok();
+ pub fn restore_cursor(&mut self) {
+ if self.terminal.cursor_visibility() == CursorVisibility::Hidden {
+ // TODO: set cursor to block
}
}
pub fn load_cursor(&mut self) {
- if self.terminal.cursor_kind() == CursorKind::Hidden {
- self.terminal.backend_mut().hide_cursor().ok();
+ if self.terminal.cursor_visibility() == CursorVisibility::Hidden {
+ // TODO: hide cursor again
}
}
@@ -177,24 +178,45 @@ impl Compositor {
}
pub fn render(&mut self, cx: &mut Context) {
- self.terminal
- .autoresize()
- .expect("Unable to determine terminal size");
+ // self.terminal
+ // .autoresize()
+ // .expect("Unable to determine terminal size");
// TODO: need to recalculate view tree if necessary
- let surface = self.terminal.current_buffer_mut();
+ let area = self.size();
- let area = *surface.area();
+ if (area.width as usize, area.height as usize) != self.surface.dimensions() {
+ self.surface
+ .resize(area.width as usize, area.height as usize);
+ }
for layer in &mut self.layers {
- layer.render(area, surface, cx);
+ layer.render(area, &mut self.surface, cx)
}
+ // TODO use kind
let (pos, kind) = self.cursor(area, cx.editor);
- let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));
+ let pos = pos.map(|pos| (pos.col, pos.row));
+
+ use termwiz::surface::{Change, Position};
+ if let Some(pos) = pos {
+ self.terminal
+ .add_change(Change::CursorVisibility(CursorVisibility::Visible));
+ self.terminal.add_change(Change::CursorPosition {
+ x: Position::Absolute(pos.0),
+ y: Position::Absolute(pos.1),
+ });
+ } else {
+ self.terminal
+ .add_change(Change::CursorVisibility(CursorVisibility::Hidden));
+ }
+
+ self.terminal.draw_from_screen(&self.surface, 0, 0);
+ self.terminal.flush().expect("failed to flush");
- self.terminal.draw(pos, kind).unwrap();
+ self.surface
+ .flush_changes_older_than(self.surface.current_seqno());
}
pub fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index 1ee4a01a..2881dd60 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -1,7 +1,7 @@
use crate::compositor::{Component, Context, EventResult};
use crossterm::event::{Event, KeyCode, KeyEvent};
use helix_view::editor::CompleteAction;
-use tui::buffer::Buffer as Surface;
+use tui::buffer::{Buffer as Surface, SurfaceExt};
use std::borrow::Cow;
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 31a9bfc8..967c5193 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -28,7 +28,7 @@ use helix_view::{
use std::borrow::Cow;
use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
-use tui::buffer::Buffer as Surface;
+use tui::buffer::{Buffer as Surface, SurfaceExt};
pub struct EditorView {
pub keymaps: Keymaps,
@@ -136,7 +136,8 @@ impl EditorView {
let x = area.right();
let border_style = theme.get("ui.window");
for y in area.top()..area.bottom() {
- surface[(x, y)]
+ surface
+ .get_mut(x, y)
.set_symbol(tui::symbols::line::VERTICAL)
//.set_symbol(" ")
.set_style(border_style);
@@ -437,7 +438,8 @@ impl EditorView {
.add_modifier(Modifier::DIM)
});
- surface[(viewport.x + pos.col as u16, viewport.y + pos.row as u16)]
+ surface
+ .get_mut(viewport.x + pos.col as u16, viewport.y + pos.row as u16)
.set_style(style);
}
}
@@ -1207,7 +1209,10 @@ impl Component for EditorView {
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
// clear with background color
- surface.set_style(area, cx.editor.theme.get("ui.background"));
+ let bg = cx.editor.theme.get("ui.background");
+ surface.add_change(termwiz::surface::Change::ClearScreen(
+ bg.bg.expect("no bg color set!").into(),
+ ));
// if the terminal size suddenly changed, we need to trigger a resize
cx.editor.resize(area.clip_bottom(1)); // -1 from bottom for commandline
diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs
index 272244c1..ce1bf696 100644
--- a/helix-term/src/ui/info.rs
+++ b/helix-term/src/ui/info.rs
@@ -1,7 +1,7 @@
use crate::compositor::{Component, Context};
use helix_view::graphics::{Margin, Rect};
use helix_view::info::Info;
-use tui::buffer::Buffer as Surface;
+use tui::buffer::{Buffer as Surface, SurfaceExt};
use tui::widgets::{Block, Borders, Paragraph, Widget};
impl Component for Info {
diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs
index d67a429e..8133cba8 100644
--- a/helix-term/src/ui/menu.rs
+++ b/helix-term/src/ui/menu.rs
@@ -3,7 +3,10 @@ use crate::{
ctrl, key, shift,
};
use crossterm::event::Event;
-use tui::{buffer::Buffer as Surface, widgets::Table};
+use tui::{
+ buffer::{Buffer as Surface, SurfaceExt},
+ widgets::Table,
+};
pub use tui::widgets::{Cell, Row};
@@ -320,7 +323,7 @@ impl<T: Item + 'static> Component for Menu<T> {
let is_marked = i >= scroll_line && i < scroll_line + scroll_height;
if !fits && is_marked {
- let cell = &mut surface[(area.x + area.width - 2, area.y + i as u16)];
+ let cell = surface.get_mut(area.x + area.width - 2, area.y + i as u16);
cell.set_symbol("▐");
// cell.set_style(selected);
// cell.set_style(if is_marked { selected } else { style });
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 3f2da92f..e316fca2 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -5,7 +5,7 @@ use crate::{
};
use crossterm::event::Event;
use tui::{
- buffer::Buffer as Surface,
+ buffer::{Buffer as Surface, SurfaceExt},
widgets::{Block, BorderType, Borders},
};
@@ -579,9 +579,8 @@ impl<T: 'static> Component for Picker<T> {
let sep_style = Style::default().fg(Color::Rgb(90, 89, 119));
let borders = BorderType::line_symbols(BorderType::Plain);
for x in inner.left()..inner.right() {
- if let Some(cell) = surface.get_mut(x, inner.y + 1) {
- cell.set_symbol(borders.horizontal).set_style(sep_style);
- }
+ let cell = surface.get_mut(x, inner.y + 1);
+ cell.set_symbol(borders.horizontal).set_style(sep_style);
}
// -- Render the contents:
diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs
index 45527482..6199723d 100644
--- a/helix-term/src/ui/popup.rs
+++ b/helix-term/src/ui/popup.rs
@@ -3,7 +3,7 @@ use crate::{
ctrl, key,
};
use crossterm::event::Event;
-use tui::buffer::Buffer as Surface;
+use tui::buffer::{Buffer as Surface, SurfaceExt};
use helix_core::Position;
use helix_view::graphics::{Margin, Rect};
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index c3402f02..e224180a 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -4,7 +4,7 @@ use crossterm::event::Event;
use helix_view::input::KeyEvent;
use helix_view::keyboard::{KeyCode, KeyModifiers};
use std::{borrow::Cow, ops::RangeFrom};
-use tui::buffer::Buffer as Surface;
+use tui::buffer::{Buffer as Surface, SurfaceExt};
use tui::widgets::{Block, Borders, Widget};
use helix_core::{
diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml
index e4cfbe4c..29557f82 100644
--- a/helix-tui/Cargo.toml
+++ b/helix-tui/Cargo.toml
@@ -12,14 +12,12 @@ repository = "https://github.com/helix-editor/helix"
homepage = "https://helix-editor.com"
include = ["src/**/*", "README.md"]
-[features]
-default = ["crossterm"]
-
[dependencies]
bitflags = "1.3"
cassowary = "0.3"
unicode-segmentation = "1.9"
crossterm = { version = "0.23", optional = true }
+termwiz = "0.15"
serde = { version = "1", "optional" = true, features = ["derive"]}
helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
helix-core = { version = "0.6", path = "../helix-core" }
diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs
deleted file mode 100644
index eff098b3..00000000
--- a/helix-tui/src/backend/crossterm.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-use crate::{backend::Backend, buffer::Cell};
-use crossterm::{
- cursor::{CursorShape, Hide, MoveTo, SetCursorShape, Show},
- execute, queue,
- style::{
- Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor,
- SetForegroundColor,
- },
- terminal::{self, Clear, ClearType},
-};
-use helix_view::graphics::{Color, CursorKind, Modifier, Rect};
-use std::io::{self, Write};
-
-pub struct CrosstermBackend<W: Write> {
- buffer: W,
-}
-
-impl<W> CrosstermBackend<W>
-where
- W: Write,
-{
- pub fn new(buffer: W) -> CrosstermBackend<W> {
- CrosstermBackend { buffer }
- }
-}
-
-impl<W> Write for CrosstermBackend<W>
-where
- W: Write,
-{
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.buffer.write(buf)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- self.buffer.flush()
- }
-}
-
-impl<W> Backend for CrosstermBackend<W>
-where
- W: Write,
-{
- fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
- where
- I: Iterator<Item = (u16, u16, &'a Cell)>,
- {
- let mut fg = Color::Reset;
- let mut bg = Color::Reset;
- let mut modifier = Modifier::empty();
- let mut last_pos: Option<(u16, u16)> = None;
- for (x, y, cell) in content {
- // Move the cursor if the previous location was not (x - 1, y)
- if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) {
- map_error(queue!(self.buffer, MoveTo(x, y)))?;
- }
- last_pos = Some((x, y));
- if cell.modifier != modifier {
- let diff = ModifierDiff {
- from: modifier,
- to: cell.modifier,
- };
- diff.queue(&mut self.buffer)?;
- modifier = cell.modifier;
- }
- if cell.fg != fg {
- let color = CColor::from(cell.fg);
- map_error(queue!(self.buffer, SetForegroundColor(color)))?;
- fg = cell.fg;
- }
- if cell.bg != bg {
- let color = CColor::from(cell.bg);
- map_error(queue!(self.buffer, SetBackgroundColor(color)))?;
- bg = cell.bg;
- }
-
- map_error(queue!(self.buffer, Print(&cell.symbol)))?;
- }
-
- map_error(queue!(
- self.buffer,
- SetForegroundColor(CColor::Reset),
- SetBackgroundColor(CColor::Reset),
- SetAttribute(CAttribute::Reset)
- ))
- }
-
- fn hide_cursor(&mut self) -> io::Result<()> {
- map_error(execute!(self.buffer, Hide))
- }
-
- fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
- let shape = match kind {
- CursorKind::Block => CursorShape::Block,
- CursorKind::Bar => CursorShape::Line,
- CursorKind::Underline => CursorShape::UnderScore,
- CursorKind::Hidden => unreachable!(),
- };
- map_error(execute!(self.buffer, Show, SetCursorShape(shape)))
- }
-
- fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
- crossterm::cursor::position()
- .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
- }
-
- fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
- map_error(execute!(self.buffer, MoveTo(x, y)))
- }
-
- fn clear(&mut self) -> io::Result<()> {
- map_error(execute!(self.buffer, Clear(ClearType::All)))
- }
-
- fn size(&self) -> io::Result<Rect> {
- let (width, height) =
- terminal::size().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
-
- Ok(Rect::new(0, 0, width, height))
- }
-
- fn flush(&mut self) -> io::Result<()> {
- self.buffer.flush()
- }
-}
-
-fn map_error(error: crossterm::Result<()>) -> io::Result<()> {
- error.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
-}
-
-#[derive(Debug)]
-struct ModifierDiff {
- pub from: Modifier,
- pub to: Modifier,
-}
-
-impl ModifierDiff {
- fn queue<W>(&self, mut w: W) -> io::Result<()>
- where
- W: io::Write,
- {
- //use crossterm::Attribute;
- let removed = self.from - self.to;
- if removed.contains(Modifier::REVERSED) {
- map_error(queue!(w, SetAttribute(CAttribute::NoReverse)))?;
- }
- if removed.contains(Modifier::BOLD) {
- map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?;
- if self.to.contains(Modifier::DIM) {
- map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
- }
- }
- if removed.contains(Modifier::ITALIC) {
- map_error(queue!(w, SetAttribute(CAttribute::NoItalic)))?;
- }
- if removed.contains(Modifier::UNDERLINED) {
- map_error(queue!(w, SetAttribute(CAttribute::NoUnderline)))?;
- }
- if removed.contains(Modifier::DIM) {
- map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?;
- }
- if removed.contains(Modifier::CROSSED_OUT) {
- map_error(queue!(w, SetAttribute(CAttribute::NotCrossedOut)))?;
- }
- if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
- map_error(queue!(w, SetAttribute(CAttribute::NoBlink)))?;
- }
-
- let added = self.to - self.from;
- if added.contains(Modifier::REVERSED) {
- map_error(queue!(w, SetAttribute(CAttribute::Reverse)))?;
- }
- if added.contains(Modifier::BOLD) {
- map_error(queue!(w, SetAttribute(CAttribute::Bold)))?;
- }
- if added.contains(Modifier::ITALIC) {
- map_error(queue!(w, SetAttribute(CAttribute::Italic)))?;
- }
- if added.contains(Modifier::UNDERLINED) {
- map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?;
- }
- if added.contains(Modifier::DIM) {
- map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
- }
- if added.contains(Modifier::CROSSED_OUT) {
- map_error(queue!(w, SetAttribute(CAttribute::CrossedOut)))?;
- }
- if added.contains(Modifier::SLOW_BLINK) {
- map_error(queue!(w, SetAttribute(CAttribute::SlowBlink)))?;
- }
- if added.contains(Modifier::RAPID_BLINK) {
- map_error(queue!(w, SetAttribute(CAttribute::RapidBlink)))?;
- }
-
- Ok(())
- }
-}
diff --git a/helix-tui/src/backend/mod.rs b/helix-tui/src/backend/mod.rs
deleted file mode 100644
index c6c11019..00000000
--- a/helix-tui/src/backend/mod.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use std::io;
-
-use crate::buffer::Cell;
-
-use helix_view::graphics::{CursorKind, Rect};
-
-#[cfg(feature = "crossterm")]
-mod crossterm;
-#[cfg(feature = "crossterm")]
-pub use self::crossterm::CrosstermBackend;
-
-mod test;
-pub use self::test::TestBackend;
-
-pub trait Backend {
- fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
- where
- I: Iterator<Item = (u16, u16, &'a Cell)>;
- fn hide_cursor(&mut self) -> Result<(), io::Error>;
- fn show_cursor(&mut self, kind: CursorKind) -> Result<(), io::Error>;
- fn get_cursor(&mut self) -> Result<(u16, u16), io::Error>;
- fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error>;
- fn clear(&mut self) -> Result<(), io::Error>;
- fn size(&self) -> Result<Rect, io::Error>;
- fn flush(&mut self) -> Result<(), io::Error>;
-}
diff --git a/helix-tui/src/backend/test.rs b/helix-tui/src/backend/test.rs
deleted file mode 100644
index 52474148..00000000
--- a/helix-tui/src/backend/test.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use crate::{
- backend::Backend,
- buffer::{Buffer, Cell},
-};
-use helix_core::unicode::width::UnicodeWidthStr;
-use helix_view::graphics::{CursorKind, Rect};
-use std::{fmt::Write, io};
-
-/// A backend used for the integration tests.
-#[derive(Debug)]
-pub struct TestBackend {
- width: u16,
- buffer: Buffer,
- height: u16,
- cursor: bool,
- pos: (u16, u16),
-}
-
-/// Returns a string representation of the given buffer for debugging purpose.
-fn buffer_view(buffer: &Buffer) -> String {
- let mut view = String::with_capacity(buffer.content.len() + buffer.area.height as usize * 3);
- for cells in buffer.content.chunks(buffer.area.width as usize) {
- let mut overwritten = vec![];
- let mut skip: usize = 0;
- view.push('"');
- for (x, c) in cells.iter().enumerate() {
- if skip == 0 {
- view.push_str(&c.symbol);
- } else {
- overwritten.push((x, &c.symbol))
- }
- skip = std::cmp::max(skip, c.symbol.width()).saturating_sub(1);
- }
- view.push('"');
- if !overwritten.is_empty() {
- write!(
- &mut view,
- " Hidden by multi-width symbols: {:?}",
- overwritten
- )
- .unwrap();
- }
- view.push('\n');
- }
- view
-}
-
-impl TestBackend {
- pub fn new(width: u16, height: u16) -> TestBackend {
- TestBackend {
- width,
- height,
- buffer: Buffer::empty(Rect::new(0, 0, width, height)),
- cursor: false,
- pos: (0, 0),
- }
- }
-
- pub fn buffer(&self) -> &Buffer {
- &self.buffer
- }
-
- pub fn resize(&mut self, width: u16, height: u16) {
- self.buffer.resize(Rect::new(0, 0, width, height));
- self.width = width;
- self.height = height;
- }
-
- pub fn assert_buffer(&self, expected: &Buffer) {
- assert_eq!(expected.area, self.buffer.area);
- let diff = expected.diff(&self.buffer);
- if diff.is_empty() {
- return;
- }
-
- let mut debug_info = String::from("Buffers are not equal");
- debug_info.push('\n');
- debug_info.push_str("Expected:");
- debug_info.push('\n');
- let expected_view = buffer_view(expected);
- debug_info.push_str(&expected_view);
- debug_info.push('\n');
- debug_info.push_str("Got:");
- debug_info.push('\n');
- let view = buffer_view(&self.buffer);
- debug_info.push_str(&view);
- debug_info.push('\n');
-
- debug_info.push_str("Diff:");
- debug_info.push('\n');
- let nice_diff = diff
- .iter()
- .enumerate()
- .map(|(i, (x, y, cell))| {
- let expected_cell = expected.get(*x, *y);
- format!(
- "{}: at ({}, {}) expected {:?} got {:?}",
- i, x, y, expected_cell, cell
- )
- })
- .collect::<Vec<String>>()
- .join("\n");
- debug_info.push_str(&nice_diff);
- panic!("{}", debug_info);
- }
-}
-
-impl Backend for TestBackend {
- fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
- where
- I: Iterator<Item = (u16, u16, &'a Cell)>,
- {
- for (x, y, c) in content {
- self.buffer[(x, y)] = c.clone();
- }
- Ok(())
- }
-
- fn hide_cursor(&mut self) -> Result<(), io::Error> {
- self.cursor = false;
- Ok(())
- }
-
- fn show_cursor(&mut self, _kind: CursorKind) -> Result<(), io::Error> {
- self.cursor = true;
- Ok(())
- }
-
- fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
- Ok(self.pos)
- }
-
- fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
- self.pos = (x, y);
- Ok(())
- }
-
- fn clear(&mut self) -> Result<(), io::Error> {
- self.buffer.reset();
- Ok(())
- }
-
- fn size(&self) -> Result<Rect, io::Error> {
- Ok(Rect::new(0, 0, self.width, self.height))
- }
-
- fn flush(&mut self) -> Result<(), io::Error> {
- Ok(())
- }
-}
diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs
index 22956b04..b55c22ad 100644
--- a/helix-tui/src/buffer.rs
+++ b/helix-tui/src/buffer.rs
@@ -1,282 +1,97 @@
use crate::text::{Span, Spans};
-use helix_core::unicode::width::UnicodeWidthStr;
-use std::cmp::min;
-use unicode_segmentation::UnicodeSegmentation;
use helix_view::graphics::{Color, Modifier, Rect, Style};
-/// A buffer cell
-#[derive(Debug, Clone, PartialEq)]
-pub struct Cell {
- pub symbol: String,
- pub fg: Color,
- pub bg: Color,
- pub modifier: Modifier,
-}
-
-impl Cell {
- pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
- self.symbol.clear();
- self.symbol.push_str(symbol);
- self
- }
-
- pub fn set_char(&mut self, ch: char) -> &mut Cell {
- self.symbol.clear();
- self.symbol.push(ch);
- self
- }
+pub use termwiz::surface::Surface as Buffer;
+use termwiz::{cell::*, surface::*};
- pub fn set_fg(&mut self, color: Color) -> &mut Cell {
- self.fg = color;
- self
- }
+pub struct Cell<'a> {
+ surface: &'a mut Surface,
+}
- pub fn set_bg(&mut self, color: Color) -> &mut Cell {
- self.bg = color;
+impl<'a> Cell<'a> {
+ pub fn set_symbol<'b>(self, symbol: &'b str) -> Cell<'a> {
+ self.surface.add_change(Change::Text(symbol.into()));
self
}
-
- pub fn set_style(&mut self, style: Style) -> &mut Cell {
- if let Some(c) = style.fg {
- self.fg = c;
- }
- if let Some(c) = style.bg {
- self.bg = c;
- }
- self.modifier.insert(style.add_modifier);
- self.modifier.remove(style.sub_modifier);
+ pub fn set_style(self, style: Style) -> Cell<'a> {
+ if let Some(fg) = style.fg {
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Foreground(fg.into())));
+ }
+ if let Some(bg) = style.bg {
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Background(bg.into())));
+ }
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Intensity(
+ if style.add_modifier.contains(Modifier::BOLD) {
+ Intensity::Bold
+ } else if style.add_modifier.contains(Modifier::DIM) {
+ Intensity::Half
+ } else {
+ Intensity::Normal
+ },
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Italic(
+ style.add_modifier.contains(Modifier::ITALIC),
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Underline(
+ if style.add_modifier.contains(Modifier::UNDERLINED) {
+ Underline::Single
+ } else {
+ Underline::None
+ },
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Reverse(
+ style.add_modifier.contains(Modifier::REVERSED),
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Invisible(
+ style.add_modifier.contains(Modifier::HIDDEN),
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::StrikeThrough(
+ style.add_modifier.contains(Modifier::CROSSED_OUT),
+ )));
+
+ self.surface
+ .add_change(Change::Attribute(AttributeChange::Blink(
+ if style.add_modifier.contains(Modifier::SLOW_BLINK) {
+ Blink::Slow
+ } else if style.add_modifier.contains(Modifier::RAPID_BLINK) {
+ Blink::Rapid
+ } else {
+ Blink::None
+ },
+ )));
self
}
-
- pub fn style(&self) -> Style {
- Style::default()
- .fg(self.fg)
- .bg(self.bg)
- .add_modifier(self.modifier)
- }
-
- pub fn reset(&mut self) {
- self.symbol.clear();
- self.symbol.push(' ');
- self.fg = Color::Reset;
- self.bg = Color::Reset;
- self.modifier = Modifier::empty();
- }
-}
-
-impl Default for Cell {
- fn default() -> Cell {
- Cell {
- symbol: " ".into(),
- fg: Color::Reset,
- bg: Color::Reset,
- modifier: Modifier::empty(),
- }
- }
}
-/// A buffer that maps to the desired content of the terminal after the draw call
-///
-/// No widget in the library interacts directly with the terminal. Instead each of them is required
-/// to draw their state to an intermediate buffer. It is basically a grid where each cell contains
-/// a grapheme, a foreground color and a background color. This grid will then be used to output
-/// the appropriate escape sequences and characters to draw the UI as the user has defined it.
-///
-/// # Examples:
-///
-/// ```
-/// use helix_tui::buffer::{Buffer, Cell};
-/// use helix_view::graphics::{Rect, Color, Style, Modifier};
-///
-/// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5});
-/// buf[(0, 2)].set_symbol("x");
-/// assert_eq!(buf[(0, 2)].symbol, "x");
-/// buf.set_string(3, 0, "string", Style::default().fg(Color::Red).bg(Color::White));
-/// assert_eq!(buf[(5, 0)], Cell{
-/// symbol: String::from("r"),
-/// fg: Color::Red,
-/// bg: Color::White,
-/// modifier: Modifier::empty()
-/// });
-/// buf[(5, 0)].set_char('x');
-/// assert_eq!(buf[(5, 0)].symbol, "x");
-/// ```
-#[derive(Debug, Default, Clone, PartialEq)]
-pub struct Buffer {
- /// The area represented by this buffer
- pub area: Rect,
- /// The content of the buffer. The length of this Vec should always be equal to area.width *
- /// area.height
- pub content: Vec<Cell>,
-}
+pub trait SurfaceExt {
+ //
+ fn set_style(&mut self, area: Rect, style: Style) {}
-impl Buffer {
- /// Returns a Buffer with all cells set to the default one
- pub fn empty(area: Rect) -> Buffer {
- let cell: Cell = Default::default();
- Buffer::filled(area, &cell)
- }
+ fn clear_with(&mut self, area: Rect, style: Style) {}
- /// Returns a Buffer with all cells initialized with the attributes of the given Cell
- pub fn filled(area: Rect, cell: &Cell) -> Buffer {
- let size = area.area() as usize;
- let mut content = Vec::with_capacity(size);
- for _ in 0..size {
- content.push(cell.clone());
- }
- Buffer { area, content }
- }
-
- /// Returns a Buffer containing the given lines
- pub fn with_lines<S>(lines: Vec<S>) -> Buffer
- where
- S: AsRef<str>,
- {
- let height = lines.len() as u16;
- let width = lines
- .iter()
- .map(|i| i.as_ref().width() as u16)
- .max()
- .unwrap_or_default();
- let mut buffer = Buffer::empty(Rect {
- x: 0,
- y: 0,
- width,
- height,
- });
- for (y, line) in lines.iter().enumerate() {
- buffer.set_string(0, y as u16, line, Style::default());
- }
- buffer
- }
-
- /// Returns the content of the buffer as a slice
- pub fn content(&self) -> &[Cell] {
- &self.content
- }
-
- /// Returns the area covered by this buffer
- pub fn area(&self) -> &Rect {
- &self.area
- }
-
- /// Returns a reference to Cell at the given coordinates
- pub fn get(&self, x: u16, y: u16) -> Option<&Cell> {
- self.index_of_opt(x, y).map(|i| &self.content[i])
- }
-
- /// Returns a mutable reference to Cell at the given coordinates
- pub fn get_mut(&mut self, x: u16, y: u16) -> Option<&mut Cell> {
- self.index_of_opt(x, y).map(|i| &mut self.content[i])
- }
-
- /// Tells whether the global (x, y) coordinates are inside the Buffer's area.
- ///
- /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
- ///
- /// # Examples
- ///
- /// ```
- /// # use helix_tui::buffer::Buffer;
- /// # use helix_view::graphics::Rect;
- /// let rect = Rect::new(200, 100, 10, 10);
- /// let buffer = Buffer::empty(rect);
- /// // Global coordinates inside the Buffer's area
- /// assert!(buffer.in_bounds(209, 100));
- /// // Global coordinates outside the Buffer's area
- /// assert!(!buffer.in_bounds(210, 100));
- /// ```
- ///
- /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
- pub fn in_bounds(&self, x: u16, y: u16) -> bool {
- x >= self.area.left()
- && x < self.area.right()
- && y >= self.area.top()
- && y < self.area.bottom()
- }
-
- /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates.
- ///
- /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
- ///
- /// # Examples
- ///
- /// ```
- /// # use helix_tui::buffer::Buffer;
- /// # use helix_view::graphics::Rect;
- /// let rect = Rect::new(200, 100, 10, 10);
- /// let buffer = Buffer::empty(rect);
- /// // Global coordinates to the top corner of this Buffer's area
- /// assert_eq!(buffer.index_of(200, 100), 0);
- /// ```
- ///
- /// # Panics
- ///
- /// Panics when given an coordinate that is outside of this Buffer's area.
- pub fn index_of(&self, x: u16, y: u16) -> usize {
- debug_assert!(
- self.in_bounds(x, y),
- "Trying to access position outside the buffer: x={}, y={}, area={:?}",
- x,
- y,
- self.area
- );
- ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
- }
-
- /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates,
- /// or `None` if the coordinates are outside the buffer's area.
- fn index_of_opt(&self, x: u16, y: u16) -> Option<usize> {
- if self.in_bounds(x, y) {
- Some(self.index_of(x, y))
- } else {
- None
- }
- }
-
- /// Returns the (global) coordinates of a cell given its index
- ///
- /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
- ///
- /// # Examples
- ///
- /// ```
- /// # use helix_tui::buffer::Buffer;
- /// # use helix_view::graphics::Rect;
- /// let rect = Rect::new(200, 100, 10, 10);
- /// let buffer = Buffer::empty(rect);
- /// assert_eq!(buffer.pos_of(0), (200, 100));
- /// assert_eq!(buffer.pos_of(14), (204, 101));
- /// ```
- ///
- /// # Panics
- ///
- /// Panics when given an index that is outside the Buffer's content.
- pub fn pos_of(&self, i: usize) -> (u16, u16) {
- debug_assert!(
- i < self.content.len(),
- "Trying to get the coords of a cell outside the buffer: i={} len={}",
- i,
- self.content.len()
- );
- (
- self.area.x + i as u16 % self.area.width,
- self.area.y + i as u16 / self.area.width,
- )
- }
-
- /// Print a string, starting at the position (x, y)
- pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
+ fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
where
S: AsRef<str>,
{
self.set_stringn(x, y, string, usize::MAX, style);
}
- /// Print at most the first n characters of a string if enough space is available
- /// until the end of the line
- pub fn set_stringn<S>(
+ fn set_stringn<S>(
&mut self,
x: u16,
y: u16,
@@ -285,137 +100,9 @@ impl Buffer {
style: Style,
) -> (u16, u16)
where
- S: AsRef<str>,
- {
- self.set_string_truncated_at_end(x, y, string.as_ref(), width, style)
- }
-
- /// Print at most the first `width` characters of a string if enough space is available
- /// until the end of the line. If `ellipsis` is true appends a `…` at the end of
- /// truncated lines. If `truncate_start` is `true`, truncate the beginning of the string
- /// instead of the end.
- #[allow(clippy::too_many_arguments)]
- pub fn set_string_truncated(
- &mut self,
- x: u16,
- y: u16,
- string: &str,
- width: usize,
- style: impl Fn(usize) -> Style, // Map a grapheme's string offset to a style
- ellipsis: bool,
- truncate_start: bool,
- ) -> (u16, u16) {
- // prevent panic if out of range
- if !self.in_bounds(x, y) || width == 0 {
- return (x, y);
- }
-
- let mut index = self.index_of(x, y);
- let mut x_offset = x as usize;
- let width = if ellipsis { width - 1 } else { width };
- let graphemes = string.grapheme_indices(true);
- let max_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
- if !truncate_start {
- for (byte_offset, s) in graphemes {
- let width = s.width();
- if width == 0 {
- continue;
- }
- // `x_offset + width > max_offset` could be integer overflow on 32-bit machines if we
- // change dimenstions to usize or u32 and someone resizes the terminal to 1x2^32.
- if width > max_offset.saturating_sub(x_offset) {
- break;
- }
-
- self.content[index].set_symbol(s);
- self.content[index].set_style(style(byte_offset));
- // Reset following cells if multi-width (they would be hidden by the grapheme),
- for i in index + 1..index + width {
- self.content[i].reset();
- }
- index += width;
- x_offset += width;
- }
- if ellipsis && x_offset - (x as usize) < string.width() {
- self.content[index].set_symbol("…");
- }
- } else {
- let mut start_index = self.index_of(x, y);
- let mut index = self.index_of(max_offset as u16, y);
-
- let total_width = string.width();
- let truncated = total_width > width;
- if ellipsis && truncated {
- self.content[start_index].set_symbol("…");
- start_index += 1;
- }
- if !truncated {
- index -= width - total_width;
- }
- for (byte_offset, s) in graphemes.rev() {
- let width = s.width();
- if width == 0 {
- continue;
- }
- let start = index - width;
- if start < start_index {
- break;
- }
- self.content[start].set_symbol(s);
- self.content[start].set_style(style(byte_offset));
- for i in start + 1..index {
- self.content[i].reset();
- }
- index -= width;
- }
- }
- (x_offset as u16, y)
- }
+ S: AsRef<str>;
- /// Print at most the first `width` characters of a string if enough space is available
- /// until the end of the line.
- pub fn set_string_truncated_at_end(
- &mut self,
- x: u16,
- y: u16,
- string: &str,
- width: usize,
- style: Style,
- ) -> (u16, u16) {
- // prevent panic if out of range
- if !self.in_bounds(x, y) {
- return (x, y);
- }
-
- let mut index = self.index_of(x, y);
- let mut x_offset = x as usize;
- let max_x_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
-
- for s in string.graphemes(true) {
- let width = s.width();
- if width == 0 {
- continue;
- }
- // `x_offset + width > max_offset` could be integer overflow on 32-bit machines if we
- // change dimensions to usize or u32 and someone resizes the terminal to 1x2^32.
- if width > max_x_offset.saturating_sub(x_offset) {
- break;
- }
-
- self.content[index].set_symbol(s);
- self.content[index].set_style(style);
- // Reset following cells if multi-width (they would be hidden by the grapheme),
- for i in index + 1..index + width {
- self.content[i].reset();
- }
- index += width;
- x_offset += width;
- }
-
- (x_offset as u16, y)
- }
-
- pub fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -> (u16, u16) {
+ fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -> (u16, u16) {
let mut remaining_width = width;
let mut x = x;
for span in &spans.0 {
@@ -436,449 +123,62 @@ impl Buffer {
(x, y)
}
- pub fn set_span<'a>(&mut self, x: u16, y: u16, span: &Span<'a>, width: u16) -> (u16, u16) {
+ fn set_span<'a>(&mut self, x: u16, y: u16, span: &Span<'a>, width: u16) -> (u16, u16) {
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
}
- #[deprecated(
- since = "0.10.0",
- note = "You should use styling capabilities of `Buffer::set_style`"
- )]
- pub fn set_background(&mut self, area: Rect, color: Color) {
- for y in area.top()..area.bottom() {
- for x in area.left()..area.right() {
- self[(x, y)].set_bg(color);
- }
- }
- }
-
- pub fn set_style(&mut self, area: Rect, style: Style) {
- for y in area.top()..area.bottom() {
- for x in area.left()..area.right() {
- self[(x, y)].set_style(style);
- }
- }
- }
-
- /// Resize the buffer so that the mapped area matches the given area and that the buffer
- /// length is equal to area.width * area.height
- pub fn resize(&mut self, area: Rect) {
- let length = area.area() as usize;
- if self.content.len() > length {
- self.content.truncate(length);
- } else {
- self.content.resize(length, Default::default());
- }
- self.area = area;
- }
-
- /// Reset all cells in the buffer
- pub fn reset(&mut self) {
- for c in &mut self.content {
- c.reset();
- }
- }
-
- /// Clear an area in the buffer
- pub fn clear(&mut self, area: Rect) {
- for x in area.left()..area.right() {
- for y in area.top()..area.bottom() {
- self[(x, y)].reset();
- }
- }
- }
-
- /// Clear an area in the buffer with a default style.
- pub fn clear_with(&mut self, area: Rect, style: Style) {
- for x in area.left()..area.right() {
- for y in area.top()..area.bottom() {
- let cell = &mut self[(x, y)];
- cell.reset();
- cell.set_style(style);
- }
- }
- }
-
- /// Merge an other buffer into this one
- pub fn merge(&mut self, other: &Buffer) {
- let area = self.area.union(other.area);
- let cell: Cell = Default::default();
- self.content.resize(area.area() as usize, cell.clone());
-
- // Move original content to the appropriate space
- let size = self.area.area() as usize;
- for i in (0..size).rev() {
- let (x, y) = self.pos_of(i);
- // New index in content
- let k = ((y - area.y) * area.width + x - area.x) as usize;
- if i != k {
- self.content[k] = self.content[i].clone();
- self.content[i] = cell.clone();
- }
- }
-
- // Push content of the other buffer into this one (may erase previous
- // data)
- let size = other.area.area() as usize;
- for i in 0..size {
- let (x, y) = other.pos_of(i);
- // New index in content
- let k = ((y - area.y) * area.width + x - area.x) as usize;
- self.content[k] = other.content[i].clone();
- }
- self.area = area;
- }
-
- /// Builds a minimal sequence of coordinates and Cells necessary to update the UI from
- /// self to other.
- ///
- /// We're assuming that buffers are well-formed, that is no double-width cell is followed by
- /// a non-blank cell.
- ///
- /// # Multi-width characters handling:
- ///
- /// ```text
- /// (Index:) `01`
- /// Prev: `コ`
- /// Next: `aa`
- /// Updates: `0: a, 1: a'
- /// ```
- ///
- /// ```text
- /// (Index:) `01`
- /// Prev: `a `
- /// Next: `コ`
- /// Updates: `0: コ` (double width symbol at index 0 - skip index 1)
- /// ```
- ///
- /// ```text
- /// (Index:) `012`
- /// Prev: `aaa`
- /// Next: `aコ`
- /// Updates: `0: a, 1: コ` (double width symbol at index 1 - skip index 2)
- /// ```
- pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(u16, u16, &'a Cell)> {
- let previous_buffer = &self.content;
- let next_buffer = &other.content;
- let width = self.area.width;
-
- let mut updates: Vec<(u16, u16, &Cell)> = vec![];
- // Cells invalidated by drawing/replacing preceeding multi-width characters:
- let mut invalidated: usize = 0;
- // Cells from the current buffer to skip due to preceeding multi-width characters taking their
- // place (the skipped cells should be blank anyway):
- let mut to_skip: usize = 0;
- for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
- if (current != previous || invalidated > 0) && to_skip == 0 {
- let x = i as u16 % width;
- let y = i as u16 / width;
- updates.push((x, y, &next_buffer[i]));
- }
-
- let current_width = current.symbol.width();
- to_skip = current_width.saturating_sub(1);
-
- let affected_width = std::cmp::max(current_width, previous.symbol.width());
- invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
- }
- updates
- }
-}
-
-impl std::ops::Index<(u16, u16)> for Buffer {
- type Output = Cell;
-
- fn index(&self, (x, y): (u16, u16)) -> &Self::Output {
- let i = self.index_of(x, y);
- &self.content[i]
+ fn set_string_truncated(
+ &mut self,
+ x: u16,
+ y: u16,
+ string: &str,
+ width: usize,
+ style: impl Fn(usize) -> Style, // Map a grapheme's string offset to a style
+ ellipsis: bool,
+ truncate_start: bool,
+ ) {
+ // TODO
}
-}
-impl std::ops::IndexMut<(u16, u16)> for Buffer {
- fn index_mut(&mut self, (x, y): (u16, u16)) -> &mut Self::Output {
- let i = self.index_of(x, y);
- &mut self.content[i]
- }
+ fn get_mut(&mut self, x: u16, y: u16) -> Cell;
}
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn cell(s: &str) -> Cell {
- let mut cell = Cell::default();
- cell.set_symbol(s);
- cell
- }
-
- #[test]
- fn it_translates_to_and_from_coordinates() {
- let rect = Rect::new(200, 100, 50, 80);
- let buf = Buffer::empty(rect);
-
- // First cell is at the upper left corner.
- assert_eq!(buf.pos_of(0), (200, 100));
- assert_eq!(buf.index_of(200, 100), 0);
-
- // Last cell is in the lower right.
- assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
- assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
- }
-
- #[test]
- #[should_panic(expected = "outside the buffer")]
- #[cfg(debug_assertions)]
- fn pos_of_panics_on_out_of_bounds() {
- let rect = Rect::new(0, 0, 10, 10);
- let buf = Buffer::empty(rect);
-
- // There are a total of 100 cells; zero-indexed means that 100 would be the 101st cell.
- buf.pos_of(100);
- }
-
- #[test]
- #[should_panic(expected = "outside the buffer")]
- #[cfg(debug_assertions)]
- fn index_of_panics_on_out_of_bounds() {
- let rect = Rect::new(0, 0, 10, 10);
- let buf = Buffer::empty(rect);
-
- // width is 10; zero-indexed means that 10 would be the 11th cell.
- buf.index_of(10, 0);
- }
-
- #[test]
- fn buffer_set_string() {
- let area = Rect::new(0, 0, 5, 1);
- let mut buffer = Buffer::empty(area);
-
- // Zero-width
- buffer.set_stringn(0, 0, "aaa", 0, Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec![" "]));
-
- buffer.set_string(0, 0, "aaa", Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["aaa "]));
-
- // Width limit:
- buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["bbbb "]));
-
- buffer.set_string(0, 0, "12345", Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
-
- // Width truncation:
- buffer.set_string(0, 0, "123456", Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
- }
-
- #[test]
- fn buffer_set_string_zero_width() {
- let area = Rect::new(0, 0, 1, 1);
- let mut buffer = Buffer::empty(area);
-
- // Leading grapheme with zero width
- let s = "\u{1}a";
- buffer.set_stringn(0, 0, s, 1, Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["a"]));
-
- // Trailing grapheme with zero with
- let s = "a\u{1}";
- buffer.set_stringn(0, 0, s, 1, Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["a"]));
- }
-
- #[test]
- fn buffer_set_string_double_width() {
- let area = Rect::new(0, 0, 5, 1);
- let mut buffer = Buffer::empty(area);
- buffer.set_string(0, 0, "コン", Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
-
- // Only 1 space left.
- buffer.set_string(0, 0, "コンピ", Style::default());
- assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
- }
-
- #[test]
- fn buffer_with_lines() {
- let buffer =
- Buffer::with_lines(vec!["┌────────┐", "│コンピュ│", "│ーa 上で│", "└────────┘"]);
- assert_eq!(buffer.area.x, 0);
- assert_eq!(buffer.area.y, 0);
- assert_eq!(buffer.area.width, 10);
- assert_eq!(buffer.area.height, 4);
- }
-
- #[test]
- fn buffer_diffing_empty_empty() {
- let area = Rect::new(0, 0, 40, 40);
- let prev = Buffer::empty(area);
- let next = Buffer::empty(area);
- let diff = prev.diff(&next);
- assert_eq!(diff, vec![]);
- }
-
- #[test]
- fn buffer_diffing_empty_filled() {
- let area = Rect::new(0, 0, 40, 40);
- let prev = Buffer::empty(area);
- let next = Buffer::filled(area, Cell::default().set_symbol("a"));
- let diff = prev.diff(&next);
- assert_eq!(diff.len(), 40 * 40);
- }
-
- #[test]
- fn buffer_diffing_filled_filled() {
- let area = Rect::new(0, 0, 40, 40);
- let prev = Buffer::filled(area, Cell::default().set_symbol("a"));
- let next = Buffer::filled(area, Cell::default().set_symbol("a"));
- let diff = prev.diff(&next);
- assert_eq!(diff, vec![]);
- }
-
- #[test]
- fn buffer_diffing_single_width() {
- let prev = Buffer::with_lines(vec![
- " ",
- "┌Title─┐ ",
- "│ │ ",
- "│ │ ",
- "└──────┘ ",
- ]);
- let next = Buffer::with_lines(vec![
- " ",
- "┌TITLE─┐ ",
- "│ │ ",
- "│ │ ",
- "└──────┘ ",
- ]);
- let diff = prev.diff(&next);
- assert_eq!(
- diff,
- vec![
- (2, 1, &cell("I")),
- (3, 1, &cell("T")),
- (4, 1, &cell("L")),
- (5, 1, &cell("E")),
- ]
- );
- }
-
- #[test]
- #[rustfmt::skip]
- fn buffer_diffing_multi_width() {
- let prev = Buffer::with_lines(vec![
- "┌Title─┐ ",
- "└──────┘ ",
- ]);
- let next = Buffer::with_lines(vec![
- "┌称号──┐ ",
- "└──────┘ ",
- ]);
- let diff = prev.diff(&next);
- assert_eq!(
- diff,
- vec![
- (1, 0, &cell("称")),
- // Skipped "i"
- (3, 0, &cell("号")),
- // Skipped "l"
- (5, 0, &cell("─")),
- ]
- );
- }
-
- #[test]
- fn buffer_diffing_multi_width_offset() {
- let prev = Buffer::with_lines(vec!["┌称号──┐"]);
- let next = Buffer::with_lines(vec!["┌─称号─┐"]);
+impl SurfaceExt for termwiz::surface::Surface {
+ //
+ //fn set_style(&mut self, area: Rect, style: Style) {
+ // //
+ //}
- let diff = prev.diff(&next);
- assert_eq!(
- diff,
- vec![(1, 0, &cell("─")), (2, 0, &cell("称")), (4, 0, &cell("号")),]
- );
- }
-
- #[test]
- fn buffer_merge() {
- let mut one = Buffer::filled(
- Rect {
- x: 0,
- y: 0,
- width: 2,
- height: 2,
- },
- Cell::default().set_symbol("1"),
- );
- let two = Buffer::filled(
- Rect {
- x: 0,
- y: 2,
- width: 2,
- height: 2,
- },
- Cell::default().set_symbol("2"),
- );
- one.merge(&two);
- assert_eq!(one, Buffer::with_lines(vec!["11", "11", "22", "22"]));
- }
+ fn set_stringn<S>(
+ &mut self,
+ x: u16,
+ y: u16,
+ string: S,
+ width: usize,
+ style: Style,
+ ) -> (u16, u16)
+ where
+ S: AsRef<str>,
+ {
+ // TODO: style and limit to width
+ self.add_change(Change::CursorPosition {
+ x: Position::Absolute(x as usize),
+ y: Position::Absolute(y as usize),
+ });
+ let fg = style.fg.unwrap_or(Color::Reset);
+ self.add_change(Change::Attribute(AttributeChange::Foreground(fg.into())));
+ let bg = style.bg.unwrap_or(Color::Reset);
+ self.add_change(Change::Attribute(AttributeChange::Background(bg.into())));
+ self.add_change(Change::Text(string.as_ref().to_owned()));
- #[test]
- fn buffer_merge2() {
- let mut one = Buffer::filled(
- Rect {
- x: 2,
- y: 2,
- width: 2,
- height: 2,
- },
- Cell::default().set_symbol("1"),
- );
- let two = Buffer::filled(
- Rect {
- x: 0,
- y: 0,
- width: 2,
- height: 2,
- },
- Cell::default().set_symbol("2"),
- );
- one.merge(&two);
- assert_eq!(
- one,
- Buffer::with_lines(vec!["22 ", "22 ", " 11", " 11"])
- );
+ (0, 0)
}
- #[test]
- fn buffer_merge3() {
- let mut one = Buffer::filled(
- Rect {
- x: 3,
- y: 3,
- width: 2,
- height: 2,
- },
- Cell::default().set_symbol("1"),
- );
- let two = Buffer::filled(
- Rect {
- x: 1,
- y: 1,
- width: 3,
- height: 4,
- },
- Cell::default().set_symbol("2"),
- );
- one.merge(&two);
- let mut merged = Buffer::with_lines(vec!["222 ", "222 ", "2221", "2221"]);
- merged.area = Rect {
- x: 1,
- y: 1,
- width: 4,
- height: 4,
- };
- assert_eq!(one, merged);
+ fn get_mut(&mut self, x: u16, y: u16) -> Cell {
+ self.add_change(Change::CursorPosition {
+ x: Position::Absolute(x as usize),
+ y: Position::Absolute(y as usize),
+ });
+ Cell { surface: self }
}
}
diff --git a/helix-tui/src/lib.rs b/helix-tui/src/lib.rs
index 2636b268..8648cfbe 100644
--- a/helix-tui/src/lib.rs
+++ b/helix-tui/src/lib.rs
@@ -122,12 +122,8 @@
//! you might need a blank space somewhere, try to pass an additional constraint and don't use the
//! corresponding area.
-pub mod backend;
pub mod buffer;
pub mod layout;
pub mod symbols;
-pub mod terminal;
pub mod text;
pub mod widgets;
-
-pub use self::terminal::{Terminal, TerminalOptions, Viewport};
diff --git a/helix-tui/src/terminal.rs b/helix-tui/src/terminal.rs
deleted file mode 100644
index 22e9232f..00000000
--- a/helix-tui/src/terminal.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-use crate::{backend::Backend, buffer::Buffer};
-use helix_view::graphics::{CursorKind, Rect};
-use std::io;
-
-#[derive(Debug, Clone, PartialEq)]
-/// UNSTABLE
-enum ResizeBehavior {
- Fixed,
- Auto,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-/// UNSTABLE
-pub struct Viewport {
- area: Rect,
- resize_behavior: ResizeBehavior,
-}
-
-impl Viewport {
- /// UNSTABLE
- pub fn fixed(area: Rect) -> Viewport {
- Viewport {
- area,
- resize_behavior: ResizeBehavior::Fixed,
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-/// Options to pass to [`Terminal::with_options`]
-pub struct TerminalOptions {
- /// Viewport used to draw to the terminal
- pub viewport: Viewport,
-}
-
-/// Interface to the terminal backed by Termion
-#[derive(Debug)]
-pub struct Terminal<B>
-where
- B: Backend,
-{
- backend: B,
- /// Holds the results of the current and previous draw calls. The two are compared at the end
- /// of each draw pass to output the necessary updates to the terminal
- buffers: [Buffer; 2],
- /// Index of the current buffer in the previous array
- current: usize,
- /// Kind of cursor (hidden or others)
- cursor_kind: CursorKind,
- /// Viewport
- viewport: Viewport,
-}
-
-impl<B> Drop for Terminal<B>
-where
- B: Backend,
-{
- fn drop(&mut self) {
- // Attempt to restore the cursor state
- if self.cursor_kind == CursorKind::Hidden {
- if let Err(err) = self.show_cursor(CursorKind::Block) {
- eprintln!("Failed to show the cursor: {}", err);
- }
- }
- }
-}
-
-impl<B> Terminal<B>
-where
- B: Backend,
-{
- /// Wrapper around Terminal initialization. Each buffer is initialized with a blank string and
- /// default colors for the foreground and the background
- pub fn new(backend: B) -> io::Result<Terminal<B>> {
- let size = backend.size()?;
- Terminal::with_options(
- backend,
- TerminalOptions {
- viewport: Viewport {
- area: size,
- resize_behavior: ResizeBehavior::Auto,
- },
- },
- )
- }
-
- /// UNSTABLE
- pub fn with_options(backend: B, options: TerminalOptions) -> io::Result<Terminal<B>> {
- Ok(Terminal {
- backend,
- buffers: [
- Buffer::empty(options.viewport.area),
- Buffer::empty(options.viewport.area),
- ],
- current: 0,
- cursor_kind: CursorKind::Block,
- viewport: options.viewport,
- })
- }
-
- // /// Get a Frame object which provides a consistent view into the terminal state for rendering.
- // pub fn get_frame(&mut self) -> Frame<B> {
- // Frame {
- // terminal: self,
- // cursor_position: None,
- // }
- // }
-
- pub fn current_buffer_mut(&mut self) -> &mut Buffer {
- &mut self.buffers[self.current]
- }
-
- pub fn backend(&self) -> &B {
- &self.backend
- }
-
- pub fn backend_mut(&mut self) -> &mut B {
- &mut self.backend
- }
-
- /// Obtains a difference between the previous and the current buffer and passes it to the
- /// current backend for drawing.
- pub fn flush(&mut self) -> io::Result<()> {
- let previous_buffer = &self.buffers[1 - self.current];
- let current_buffer = &self.buffers[self.current];
- let updates = previous_buffer.diff(current_buffer);
- self.backend.draw(updates.into_iter())
- }
-
- /// Updates the Terminal so that internal buffers match the requested size. Requested size will
- /// be saved so the size can remain consistent when rendering.
- /// This leads to a full clear of the screen.
- pub fn resize(&mut self, area: Rect) -> io::Result<()> {
- self.buffers[self.current].resize(area);
- self.buffers[1 - self.current].resize(area);
- self.viewport.area = area;
- self.clear()
- }
-
- /// Queries the backend for size and resizes if it doesn't match the previous size.
- pub fn autoresize(&mut self) -> io::Result<Rect> {
- let size = self.size()?;
- if size != self.viewport.area {
- self.resize(size)?;
- };
- Ok(size)
- }
-
- /// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
- /// and prepares for the next draw call.
- pub fn draw(
- &mut self,
- cursor_position: Option<(u16, u16)>,
- cursor_kind: CursorKind,
- ) -> io::Result<()> {
- // // Autoresize - otherwise we get glitches if shrinking or potential desync between widgets
- // // and the terminal (if growing), which may OOB.
- // self.autoresize()?;
-
- // let mut frame = self.get_frame();
- // f(&mut frame);
- // // We can't change the cursor position right away because we have to flush the frame to
- // // stdout first. But we also can't keep the frame around, since it holds a &mut to
- // // Terminal. Thus, we're taking the important data out of the Frame and dropping it.
- // let cursor_position = frame.cursor_position;
-
- // Draw to stdout
- self.flush()?;
-
- if let Some((x, y)) = cursor_position {
- self.set_cursor(x, y)?;
- }
-
- match cursor_kind {
- CursorKind::Hidden => self.hide_cursor()?,
- kind => self.show_cursor(kind)?,
- }
-
- // Swap buffers
- self.buffers[1 - self.current].reset();
- self.current = 1 - self.current;
-
- // Flush
- self.backend.flush()?;
- Ok(())
- }
-
- #[inline]
- pub fn cursor_kind(&self) -> CursorKind {
- self.cursor_kind
- }
-
- pub fn hide_cursor(&mut self) -> io::Result<()> {
- self.backend.hide_cursor()?;
- self.cursor_kind = CursorKind::Hidden;
- Ok(())
- }
-
- pub fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
- self.backend.show_cursor(kind)?;
- self.cursor_kind = kind;
- Ok(())
- }
-
- pub fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
- self.backend.get_cursor()
- }
-
- pub fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
- self.backend.set_cursor(x, y)
- }
-
- /// Clear the terminal and force a full redraw on the next draw call.
- pub fn clear(&mut self) -> io::Result<()> {
- self.backend.clear()?;
- // Reset the back buffer to make sure the next update will redraw everything.
- self.buffers[1 - self.current].reset();
- Ok(())
- }
-
- /// Queries the real size of the backend.
- pub fn size(&self) -> io::Result<Rect> {
- self.backend.size()
- }
-}
diff --git a/helix-tui/src/widgets/block.rs b/helix-tui/src/widgets/block.rs
index 26223c3e..f1576bc1 100644
--- a/helix-tui/src/widgets/block.rs
+++ b/helix-tui/src/widgets/block.rs
@@ -1,5 +1,5 @@
use crate::{
- buffer::Buffer,
+ buffer::{Buffer, SurfaceExt},
symbols::line,
text::{Span, Spans},
widgets::{Borders, Widget},
@@ -134,62 +134,62 @@ impl<'a> Widget for Block<'a> {
if area.area() == 0 {
return;
}
- buf.set_style(area, self.style);
+ // buf.set_style(area, self.style);
let symbols = BorderType::line_symbols(self.border_type);
- // Sides
- if self.borders.intersects(Borders::LEFT) {
- for y in area.top()..area.bottom() {
- buf[(area.left(), y)]
- .set_symbol(symbols.vertical)
- .set_style(self.border_style);
- }
- }
- if self.borders.intersects(Borders::TOP) {
- for x in area.left()..area.right() {
- buf[(x, area.top())]
- .set_symbol(symbols.horizontal)
- .set_style(self.border_style);
- }
- }
- if self.borders.intersects(Borders::RIGHT) {
- let x = area.right() - 1;
- for y in area.top()..area.bottom() {
- buf[(x, y)]
- .set_symbol(symbols.vertical)
- .set_style(self.border_style);
- }
- }
- if self.borders.intersects(Borders::BOTTOM) {
- let y = area.bottom() - 1;
- for x in area.left()..area.right() {
- buf[(x, y)]
- .set_symbol(symbols.horizontal)
- .set_style(self.border_style);
- }
- }
+ // // Sides
+ // if self.borders.intersects(Borders::LEFT) {
+ // for y in area.top()..area.bottom() {
+ // buf[(area.left(), y)]
+ // .set_symbol(symbols.vertical)
+ // .set_style(self.border_style);
+ // }
+ // }
+ // if self.borders.intersects(Borders::TOP) {
+ // for x in area.left()..area.right() {
+ // buf[(x, area.top())]
+ // .set_symbol(symbols.horizontal)
+ // .set_style(self.border_style);
+ // }
+ // }
+ // if self.borders.intersects(Borders::RIGHT) {
+ // let x = area.right() - 1;
+ // for y in area.top()..area.bottom() {
+ // buf[(x, y)]
+ // .set_symbol(symbols.vertical)
+ // .set_style(self.border_style);
+ // }
+ // }
+ // if self.borders.intersects(Borders::BOTTOM) {
+ // let y = area.bottom() - 1;
+ // for x in area.left()..area.right() {
+ // buf[(x, y)]
+ // .set_symbol(symbols.horizontal)
+ // .set_style(self.border_style);
+ // }
+ // }
- // Corners
- if self.borders.contains(Borders::RIGHT | Borders::BOTTOM) {
- buf[(area.right() - 1, area.bottom() - 1)]
- .set_symbol(symbols.bottom_right)
- .set_style(self.border_style);
- }
- if self.borders.contains(Borders::RIGHT | Borders::TOP) {
- buf[(area.right() - 1, area.top())]
- .set_symbol(symbols.top_right)
- .set_style(self.border_style);
- }
- if self.borders.contains(Borders::LEFT | Borders::BOTTOM) {
- buf[(area.left(), area.bottom() - 1)]
- .set_symbol(symbols.bottom_left)
- .set_style(self.border_style);
- }
- if self.borders.contains(Borders::LEFT | Borders::TOP) {
- buf[(area.left(), area.top())]
- .set_symbol(symbols.top_left)
- .set_style(self.border_style);
- }
+ // // Corners
+ // if self.borders.contains(Borders::RIGHT | Borders::BOTTOM) {
+ // buf[(area.right() - 1, area.bottom() - 1)]
+ // .set_symbol(symbols.bottom_right)
+ // .set_style(self.border_style);
+ // }
+ // if self.borders.contains(Borders::RIGHT | Borders::TOP) {
+ // buf[(area.right() - 1, area.top())]
+ // .set_symbol(symbols.top_right)
+ // .set_style(self.border_style);
+ // }
+ // if self.borders.contains(Borders::LEFT | Borders::BOTTOM) {
+ // buf[(area.left(), area.bottom() - 1)]
+ // .set_symbol(symbols.bottom_left)
+ // .set_style(self.border_style);
+ // }
+ // if self.borders.contains(Borders::LEFT | Borders::TOP) {
+ // buf[(area.left(), area.top())]
+ // .set_symbol(symbols.top_left)
+ // .set_style(self.border_style);
+ // }
if let Some(title) = self.title {
let lx = if self.borders.intersects(Borders::LEFT) {
diff --git a/helix-tui/src/widgets/paragraph.rs b/helix-tui/src/widgets/paragraph.rs
index 4e839162..7f388d74 100644
--- a/helix-tui/src/widgets/paragraph.rs
+++ b/helix-tui/src/widgets/paragraph.rs
@@ -1,5 +1,5 @@
use crate::{
- buffer::Buffer,
+ buffer::{Buffer, SurfaceExt},
layout::Alignment,
text::{StyledGrapheme, Text},
widgets::{
@@ -134,7 +134,7 @@ impl<'a> Paragraph<'a> {
impl<'a> Widget for Paragraph<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
- buf.set_style(area, self.style);
+ // buf.set_style(area, self.style);
let text_area = match self.block.take() {
Some(b) => {
let inner_area = b.inner(area);
@@ -176,15 +176,15 @@ impl<'a> Widget for Paragraph<'a> {
if y >= self.scroll.0 {
let mut x = get_line_offset(current_line_width, text_area.width, self.alignment);
for StyledGrapheme { symbol, style } in current_line {
- buf[(text_area.left() + x, text_area.top() + y - self.scroll.0)]
- .set_symbol(if symbol.is_empty() {
- // If the symbol is empty, the last char which rendered last time will
- // leave on the line. It's a quick fix.
- " "
- } else {
- symbol
- })
- .set_style(*style);
+ // buf[(text_area.left() + x, text_area.top() + y - self.scroll.0)]
+ // .set_symbol(if symbol.is_empty() {
+ // // If the symbol is empty, the last char which rendered last time will
+ // // leave on the line. It's a quick fix.
+ // " "
+ // } else {
+ // symbol
+ // })
+ // .set_style(*style);
x += symbol.width() as u16;
}
}
diff --git a/helix-tui/src/widgets/table.rs b/helix-tui/src/widgets/table.rs
index 6aee5988..bfe267b4 100644
--- a/helix-tui/src/widgets/table.rs
+++ b/helix-tui/src/widgets/table.rs
@@ -1,5 +1,5 @@
use crate::{
- buffer::Buffer,
+ buffer::{Buffer, SurfaceExt},
layout::Constraint,
text::Text,
widgets::{Block, Widget},
@@ -390,7 +390,7 @@ impl<'a> Table<'a> {
if area.area() == 0 {
return;
}
- buf.set_style(area, self.style);
+ // buf.set_style(area, self.style);
let table_area = match self.block.take() {
Some(b) => {
let inner_area = b.inner(area);
@@ -410,15 +410,15 @@ impl<'a> Table<'a> {
// Draw header
if let Some(ref header) = self.header {
let max_header_height = table_area.height.min(header.total_height());
- buf.set_style(
- Rect {
- x: table_area.left(),
- y: table_area.top(),
- width: table_area.width,
- height: table_area.height.min(header.height),
- },
- header.style,
- );
+ // buf.set_style(
+ // Rect {
+ // x: table_area.left(),
+ // y: table_area.top(),
+ // width: table_area.width,
+ // height: table_area.height.min(header.height),
+ // },
+ // header.style,
+ // );
let mut col = table_area.left();
if has_selection {
col += (highlight_symbol.width() as u16).min(table_area.width);
@@ -461,7 +461,7 @@ impl<'a> Table<'a> {
width: table_area.width,
height: table_row.height,
};
- buf.set_style(table_row_area, table_row.style);
+ // buf.set_style(table_row_area, table_row.style);
let is_selected = state.selected.map(|s| s == i).unwrap_or(false);
let table_row_start_col = if has_selection {
let symbol = if is_selected {
@@ -490,14 +490,14 @@ impl<'a> Table<'a> {
col += *width + self.column_spacing;
}
if is_selected {
- buf.set_style(table_row_area, self.highlight_style);
+ // buf.set_style(table_row_area, self.highlight_style);
}
}
}
}
fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect) {
- buf.set_style(area, cell.style);
+ // buf.set_style(area, cell.style);
for (i, spans) in cell.content.lines.iter().enumerate() {
if i as u16 >= area.height {
break;
diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml
index a4fa256d..c595d414 100644
--- a/helix-view/Cargo.toml
+++ b/helix-view/Cargo.toml
@@ -10,8 +10,8 @@ repository = "https://github.com/helix-editor/helix"
homepage = "https://helix-editor.com"
[features]
-default = []
-term = ["crossterm"]
+default = ["term"]
+term = ["termwiz", "crossterm"]
[dependencies]
bitflags = "1.3"
@@ -20,6 +20,7 @@ helix-core = { version = "0.6", path = "../helix-core" }
helix-lsp = { version = "0.6", path = "../helix-lsp"}
helix-dap = { version = "0.6", path = "../helix-dap"}
crossterm = { version = "0.23", optional = true }
+termwiz = { version = "0.15", optional = true }
# Conversion traits
once_cell = "1.10"
diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs
index 6d0a9292..5c203d40 100644
--- a/helix-view/src/graphics.rs
+++ b/helix-view/src/graphics.rs
@@ -234,30 +234,30 @@ pub enum Color {
}
#[cfg(feature = "term")]
-impl From<Color> for crossterm::style::Color {
- fn from(color: Color) -> Self {
- use crossterm::style::Color as CColor;
-
- match color {
- Color::Reset => CColor::Reset,
- Color::Black => CColor::Black,
- Color::Red => CColor::DarkRed,
- Color::Green => CColor::DarkGreen,
- Color::Yellow => CColor::DarkYellow,
- Color::Blue => CColor::DarkBlue,
- Color::Magenta => CColor::DarkMagenta,
- Color::Cyan => CColor::DarkCyan,
- Color::Gray => CColor::DarkGrey,
- Color::LightRed => CColor::Red,
- Color::LightGreen => CColor::Green,
- Color::LightBlue => CColor::Blue,
- Color::LightYellow => CColor::Yellow,
- Color::LightMagenta => CColor::Magenta,
- Color::LightCyan => CColor::Cyan,
- Color::LightGray => CColor::Grey,
- Color::White => CColor::White,
- Color::Indexed(i) => CColor::AnsiValue(i),
- Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
+impl Into<termwiz::color::ColorAttribute> for Color {
+ fn into(self) -> termwiz::color::ColorAttribute {
+ use termwiz::color::{AnsiColor, ColorAttribute, RgbColor};
+ match self {
+ Color::Reset => ColorAttribute::Default,
+ Color::Black => AnsiColor::Black.into(),
+ Color::Gray | Color::LightGray => AnsiColor::Grey.into(),
+ Color::Red => AnsiColor::Maroon.into(),
+ Color::LightRed => AnsiColor::Red.into(),
+ Color::Green => AnsiColor::Green.into(),
+ Color::LightGreen => AnsiColor::Lime.into(),
+ Color::Yellow => AnsiColor::Olive.into(),
+ Color::LightYellow => AnsiColor::Yellow.into(),
+ Color::Magenta => AnsiColor::Purple.into(),
+ Color::LightMagenta => AnsiColor::Fuchsia.into(),
+ Color::Cyan => AnsiColor::Teal.into(),
+ Color::LightCyan => AnsiColor::Aqua.into(),
+ Color::White => AnsiColor::White.into(),
+ Color::Blue => AnsiColor::Navy.into(),
+ Color::LightBlue => AnsiColor::Blue.into(),
+ Color::Indexed(i) => ColorAttribute::PaletteIndex(i),
+ Color::Rgb(r, g, b) => {
+ ColorAttribute::TrueColorWithDefaultFallback(RgbColor::new_8bpc(r, g, b))
+ }
}
}
}