aboutsummaryrefslogtreecommitdiff
path: root/vendor/console
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/console
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/console')
-rw-r--r--vendor/console/.cargo-checksum.json1
-rw-r--r--vendor/console/CHANGELOG.md103
-rw-r--r--vendor/console/Cargo.lock272
-rw-r--r--vendor/console/Cargo.toml72
-rw-r--r--vendor/console/LICENSE22
-rw-r--r--vendor/console/Makefile39
-rw-r--r--vendor/console/README.md76
-rw-r--r--vendor/console/benches/ansi_parser.rs27
-rw-r--r--vendor/console/examples/colors.rs14
-rw-r--r--vendor/console/examples/colors256.rs17
-rw-r--r--vendor/console/examples/cursor_at.rs30
-rw-r--r--vendor/console/examples/term.rs33
-rwxr-xr-xvendor/console/scripts/wasmtime-wrapper.sh4
-rw-r--r--vendor/console/src/ansi.rs438
-rw-r--r--vendor/console/src/common_term.rs72
-rw-r--r--vendor/console/src/kb.rs29
-rw-r--r--vendor/console/src/lib.rs104
-rw-r--r--vendor/console/src/term.rs632
-rw-r--r--vendor/console/src/unix_term.rs362
-rw-r--r--vendor/console/src/utils.rs962
-rw-r--r--vendor/console/src/wasm_term.rs54
-rw-r--r--vendor/console/src/windows_term/colors.rs451
-rw-r--r--vendor/console/src/windows_term/mod.rs563
-rwxr-xr-xvendor/console/tests/data/sample_zellij_session.log56
24 files changed, 4433 insertions, 0 deletions
diff --git a/vendor/console/.cargo-checksum.json b/vendor/console/.cargo-checksum.json
new file mode 100644
index 0000000..ab16746
--- /dev/null
+++ b/vendor/console/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"705c0d0cea7a8c090d679ac9399d9dcc31ed15c40eb571d91f03947ad86efe70","Cargo.lock":"3f19acf9c8353b6860267b136400702ec92486f700fc49d18f8862c9db056157","Cargo.toml":"9dccef268d95485475a4be6261bc2f51af881c6e78187dc881afa01621a23e01","LICENSE":"2022c11b24fc1e50fa06a5959e40198a3464f560729ad3d00139abb8a328362d","Makefile":"e708c1503cf3c5e6a218cdd703467f3ff7155f52b5a17950c6ecdf889a9e1338","README.md":"4f2d74ab86b2c1c517aafee98a8d7e53c0da38e1a4b765c7f1a8e0d6e7b1d07a","benches/ansi_parser.rs":"d323989ce68c1a1c0b6cd10f573e4dde11836a168f1b1bbe6c9c21b8db082629","examples/colors.rs":"395c5f8c4950830f3f960ed9b5522e93d5fdbda1d9c3d16c77144f4d87b503c7","examples/colors256.rs":"523682df72f7a4fe398e8d681661a007b96ad52d0c4c963cb22b07b85b1b4f2d","examples/cursor_at.rs":"9e21cedb6d0211fab831c71131bbf5e54e6df8e50129c36faee8957a45c33e1f","examples/term.rs":"10739f800cf0548bb8639d809c1f32b886f27ae4ba716d5db642fb6f37f580c4","scripts/wasmtime-wrapper.sh":"f7cbec1e6de7a456880c737ee264f2d010a40e6e80b4241e4099fb149389291a","src/ansi.rs":"b42b6806a7ca971e3ce2f307b74a3e7ff3f867b67cb342209b1153c0fed2e292","src/common_term.rs":"c924164ac0731148f3c696af0a9af5759465c959631d12fb90462a515e8c186c","src/kb.rs":"72f56323ca7b6741ddb2982c67610fe944cd58095051d84403a15d6e061422d0","src/lib.rs":"1bc9f12672660506c74a313132bb3d2f4fdec550b46c6df75b1cc8124b13d583","src/term.rs":"390403b40053f484a446795198dbaaf9774ea678f153bb19cd70d247b5e01947","src/unix_term.rs":"a1f70fcca92fa019159a7da82d52c62c242acfa51ba85ebae81623c7e6e603aa","src/utils.rs":"1d481c0f6593b29155238fd335f18096b14988fcb725eb2d82667b8f324ac6f6","src/wasm_term.rs":"43b9968ef158df93a269b62d5224837c10ef4d0b7f9c7a3253f8ad099a448af5","src/windows_term/colors.rs":"4c7a53c2341502965dc339f7cd9df54bf3f0b9fbddbba5158757743900e028c5","src/windows_term/mod.rs":"3da9e7e19b9ec0bd172403c8f142192f3e418210d3e28858f363a61e8a041eaf","tests/data/sample_zellij_session.log":"341286d8721451c0a7dc647e7b98438191651199b2d5f03d132e3e971aeddadb"},"package":"c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"} \ No newline at end of file
diff --git a/vendor/console/CHANGELOG.md b/vendor/console/CHANGELOG.md
new file mode 100644
index 0000000..1f13206
--- /dev/null
+++ b/vendor/console/CHANGELOG.md
@@ -0,0 +1,103 @@
+# Changelog
+
+## 0.15.7
+
+### Enhancements
+
+* Set an appropriate lower version of libc for macos changes.
+* Improved behavior of `read_single_key` so it does not disturb other
+ threads quite as much. (#165)
+* More reliably reset raw mode in terminal. (#171)
+
+## 0.15.6
+
+### Enhancements
+
+* Switch to `select()` on macOS for polling on TTYs to work around
+ a macOS bug. (#169)
+* Added blink fast and strikethrough attributes. (#159)
+
+## 0.15.5
+
+### Enhancements
+
+* Removed `regex` dependency. (#153)
+* Clarified that `clicolors-control` is no longer used.
+* Handle non-tty terminals in `read_char`. (#124)
+
+## 0.15.4
+
+### Enhancements
+
+* Fix for regression where console size was misreported on windows. (#151)
+
+## 0.15.3
+
+### Enhancements
+
+* Dropped `terminal_size` dependency.
+
+## 0.15.2
+
+### Enhancements
+
+* Dropped `once_cell` dependency to support MSRV again.
+
+## 0.15.1
+
+### Enhancements
+
+* ANSI support no longer depends on `regex` crate.
+* Crate now supports `minver`.
+
+## 0.15.0
+
+### Enhancements
+
+* Added more key recognitions
+* Exposed `pad_str_with` to public API
+* Added `ReadWritePair`
+* Support `color256` in `Style::from_dotted_str`
+
+### BREAKING
+
+* Added `ReadWritePair` to `TermTarget` to allow arbitrary read write pairs behave as a term
+* Removed `Copy` and `PartialEq` from `TermTarget`
+
+## 0.14.1
+
+### Enhancements
+
+* Added `NO_COLOR` support
+* Added some more key recognitions
+* Undeprecate `Term::is_term`
+
+## 0.14.0
+
+### Enhancements
+
+* Added emoji support for newer Windows terminals.
+
+### BREAKING
+
+* Made the windows terminal emulation a non default feature (`windows-console-colors`)
+
+## 0.13.0
+
+### Enhancements
+
+* Added `user_attended_stderr` for checking if stderr is a terminal
+* Removed `termios` dependency
+
+### Bug Fixes
+
+* Better handling of key recognition on unix
+* `Term::terminal_size()` on stderr terms correctly returns stderr term info
+
+### Deprecated
+
+* Deprecate `Term::is_term()` in favor of `Term::features().is_attended()`
+
+### BREAKING
+
+* Remove `Term::want_emoji()` in favor of `Term::features().wants_emoji()`
diff --git a/vendor/console/Cargo.lock b/vendor/console/Cargo.lock
new file mode 100644
index 0000000..22334e6
--- /dev/null
+++ b/vendor/console/Cargo.lock
@@ -0,0 +1,272 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console"
+version = "0.15.7"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "proptest",
+ "regex",
+ "unicode-width",
+ "windows-sys",
+]
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "getrandom"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proptest"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "byteorder",
+ "lazy_static",
+ "num-traits",
+ "quick-error",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+]
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
diff --git a/vendor/console/Cargo.toml b/vendor/console/Cargo.toml
new file mode 100644
index 0000000..85849e1
--- /dev/null
+++ b/vendor/console/Cargo.toml
@@ -0,0 +1,72 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.48.0"
+name = "console"
+version = "0.15.7"
+authors = ["Armin Ronacher <armin.ronacher@active-4.com>"]
+description = "A terminal and console abstraction for Rust"
+homepage = "https://github.com/console-rs/console"
+documentation = "https://docs.rs/console"
+readme = "README.md"
+keywords = [
+ "cli",
+ "terminal",
+ "colors",
+ "console",
+ "ansi",
+]
+license = "MIT"
+repository = "https://github.com/console-rs/console"
+
+[dependencies.lazy_static]
+version = "1.4.0"
+
+[dependencies.libc]
+version = "0.2.99"
+
+[dependencies.unicode-width]
+version = "0.1"
+optional = true
+
+[dev-dependencies.proptest]
+version = "1.0.0"
+features = [
+ "std",
+ "bit-set",
+ "break-dead-code",
+]
+default-features = false
+
+[dev-dependencies.regex]
+version = "1.4.2"
+
+[features]
+ansi-parsing = []
+default = [
+ "unicode-width",
+ "ansi-parsing",
+]
+windows-console-colors = ["ansi-parsing"]
+
+[target."cfg(windows)".dependencies.encode_unicode]
+version = "0.3"
+
+[target."cfg(windows)".dependencies.windows-sys]
+version = "0.45.0"
+features = [
+ "Win32_Foundation",
+ "Win32_System_Console",
+ "Win32_Storage_FileSystem",
+ "Win32_UI_Input_KeyboardAndMouse",
+]
diff --git a/vendor/console/LICENSE b/vendor/console/LICENSE
new file mode 100644
index 0000000..dc9a85c
--- /dev/null
+++ b/vendor/console/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Armin Ronacher <armin.ronacher@active-4.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/console/Makefile b/vendor/console/Makefile
new file mode 100644
index 0000000..ccab99d
--- /dev/null
+++ b/vendor/console/Makefile
@@ -0,0 +1,39 @@
+all: test
+
+check:
+ @cargo check --all-features
+
+build:
+ @cargo build --all-features
+
+doc:
+ @cargo doc --all-features
+
+test:
+ @echo "CARGO TESTS"
+ @cargo test
+ @cargo test --all-features
+ @cargo test --no-default-features
+
+check-minver:
+ @echo "MINVER CHECK"
+ @cargo minimal-versions check
+ @cargo minimal-versions check --all-features
+ @cargo minimal-versions check --no-default-features
+
+format:
+ @rustup component add rustfmt 2> /dev/null
+ @cargo fmt --all
+
+format-check:
+ @rustup component add rustfmt 2> /dev/null
+ @cargo fmt --all -- --check
+
+lint:
+ @rustup component add clippy 2> /dev/null
+ @cargo clippy --examples --tests
+
+msrv-lock:
+ @cargo update -p proptest --precise=1.0.0
+
+.PHONY: all doc build check test format format-check lint check-minver msrv-lock
diff --git a/vendor/console/README.md b/vendor/console/README.md
new file mode 100644
index 0000000..9a046d8
--- /dev/null
+++ b/vendor/console/README.md
@@ -0,0 +1,76 @@
+# `console`
+
+[![Build Status](https://github.com/console-rs/console/workflows/CI/badge.svg?branch=master)](https://github.com/console-rs/console/actions?query=workflow%3ACI)
+[![Crates.io](https://img.shields.io/crates/d/console.svg)](https://crates.io/crates/console)
+[![License](https://img.shields.io/github/license/console-rs/console)](https://github.com/console-rs/console/blob/master/LICENSE)
+[![rustc 1.48.0](https://img.shields.io/badge/rust-1.48%2B-orange.svg)](https://img.shields.io/badge/rust-1.48%2B-orange.svg)
+[![Documentation](https://docs.rs/console/badge.svg)](https://docs.rs/console)
+
+**console** is a library for Rust that provides access to various terminal
+features so you can build nicer looking command line interfaces. It
+comes with various tools and utilities for working with Terminals and
+formatting text.
+
+Best paired with other libraries in the family:
+
+* [dialoguer](https://docs.rs/dialoguer)
+* [indicatif](https://docs.rs/indicatif)
+
+## Terminal Access
+
+The terminal is abstracted through the `console::Term` type. It can
+either directly provide access to the connected terminal or by buffering
+up commands. A buffered terminal will however not be completely buffered
+on windows where cursor movements are currently directly passed through.
+
+Example usage:
+
+```rust
+use std::thread;
+use std::time::Duration;
+
+use console::Term;
+
+let term = Term::stdout();
+term.write_line("Hello World!")?;
+thread::sleep(Duration::from_millis(2000));
+term.clear_line()?;
+```
+
+## Colors and Styles
+
+`console` automaticaly detects when to use colors based on the tty flag. It also
+provides higher level wrappers for styling text and other things that can be
+displayed with the `style` function and utility types.
+
+Example usage:
+
+```rust
+use console::style;
+
+println!("This is {} neat", style("quite").cyan());
+```
+
+You can also store styles and apply them to text later:
+
+```rust
+use console::Style;
+
+let cyan = Style::new().cyan();
+println!("This is {} neat", cyan.apply_to("quite"));
+```
+
+## Working with ANSI Codes
+
+The crate provides the function `strip_ansi_codes` to remove ANSI codes
+from a string as well as `measure_text_width` to calculate the width of a
+string as it would be displayed by the terminal. Both of those together
+are useful for more complex formatting.
+
+## Unicode Width Support
+
+By default this crate depends on the `unicode-width` crate to calculate
+the width of terminal characters. If you do not need this you can disable
+the `unicode-width` feature which will cut down on dependencies.
+
+License: MIT
diff --git a/vendor/console/benches/ansi_parser.rs b/vendor/console/benches/ansi_parser.rs
new file mode 100644
index 0000000..2d74c42
--- /dev/null
+++ b/vendor/console/benches/ansi_parser.rs
@@ -0,0 +1,27 @@
+use console::{strip_ansi_codes, AnsiCodeIterator};
+use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
+
+use std::{fs, path::Path};
+
+pub fn parse_throughput(c: &mut Criterion) {
+ let session_log_path = Path::new("tests")
+ .join("data")
+ .join("sample_zellij_session.log");
+ let session_log = fs::read_to_string(session_log_path).unwrap();
+
+ let mut group = c.benchmark_group("ansi-parsing");
+ group.throughput(Throughput::Bytes(session_log.len() as u64));
+ group.bench_function("parse", |b| {
+ b.iter(|| {
+ let v: Vec<_> = AnsiCodeIterator::new(&session_log).collect();
+ black_box(v);
+ })
+ });
+ group.bench_function("strip", |b| {
+ b.iter(|| black_box(strip_ansi_codes(&session_log)))
+ });
+ group.finish();
+}
+
+criterion_group!(throughput, parse_throughput);
+criterion_main!(throughput);
diff --git a/vendor/console/examples/colors.rs b/vendor/console/examples/colors.rs
new file mode 100644
index 0000000..69c51fd
--- /dev/null
+++ b/vendor/console/examples/colors.rs
@@ -0,0 +1,14 @@
+use console::style;
+
+fn main() {
+ println!(
+ "This is red on black: {:010x}",
+ style(42).red().on_black().bold()
+ );
+ println!("This is reversed: [{}]", style("whatever").reverse());
+ println!("This is cyan: {}", style("whatever").cyan());
+ eprintln!(
+ "This is black bright: {}",
+ style("whatever").for_stderr().bright().black()
+ );
+}
diff --git a/vendor/console/examples/colors256.rs b/vendor/console/examples/colors256.rs
new file mode 100644
index 0000000..60acb88
--- /dev/null
+++ b/vendor/console/examples/colors256.rs
@@ -0,0 +1,17 @@
+use console::style;
+
+fn main() {
+ for i in 0..=255 {
+ print!("{:03} ", style(i).color256(i));
+ if i % 16 == 15 {
+ println!();
+ }
+ }
+
+ for i in 0..=255 {
+ print!("{:03} ", style(i).black().on_color256(i));
+ if i % 16 == 15 {
+ println!();
+ }
+ }
+}
diff --git a/vendor/console/examples/cursor_at.rs b/vendor/console/examples/cursor_at.rs
new file mode 100644
index 0000000..9a1bb3d
--- /dev/null
+++ b/vendor/console/examples/cursor_at.rs
@@ -0,0 +1,30 @@
+extern crate console;
+
+use std::io;
+use std::thread;
+use std::time::Duration;
+
+use console::{style, Term};
+
+fn write_chars() -> io::Result<()> {
+ let term = Term::stdout();
+ let (heigth, width) = term.size();
+ for x in 0..width {
+ for y in 0..heigth {
+ term.move_cursor_to(x as usize, y as usize)?;
+ let text = if (x + y) % 2 == 0 {
+ format!("{}", style(x % 10).black().on_red())
+ } else {
+ format!("{}", style(x % 10).red().on_black())
+ };
+
+ term.write_str(&text)?;
+ thread::sleep(Duration::from_micros(600));
+ }
+ }
+ Ok(())
+}
+
+fn main() {
+ write_chars().unwrap();
+}
diff --git a/vendor/console/examples/term.rs b/vendor/console/examples/term.rs
new file mode 100644
index 0000000..a0637f7
--- /dev/null
+++ b/vendor/console/examples/term.rs
@@ -0,0 +1,33 @@
+use std::io::{self, Write};
+use std::thread;
+use std::time::Duration;
+
+use console::{style, Term};
+
+fn do_stuff() -> io::Result<()> {
+ let term = Term::stdout();
+ term.set_title("Counting...");
+ term.write_line("Going to do some counting now")?;
+ term.hide_cursor()?;
+ for x in 0..10 {
+ if x != 0 {
+ term.move_cursor_up(1)?;
+ }
+ term.write_line(&format!("Counting {}/10", style(x + 1).cyan()))?;
+ thread::sleep(Duration::from_millis(400));
+ }
+ term.show_cursor()?;
+ term.clear_last_lines(1)?;
+ term.write_line("Done counting!")?;
+ writeln!(&term, "Hello World!")?;
+
+ write!(&term, "To edit: ")?;
+ let res = term.read_line_initial_text("default")?;
+ writeln!(&term, "\n{}", res)?;
+
+ Ok(())
+}
+
+fn main() {
+ do_stuff().unwrap();
+}
diff --git a/vendor/console/scripts/wasmtime-wrapper.sh b/vendor/console/scripts/wasmtime-wrapper.sh
new file mode 100755
index 0000000..75d8ca5
--- /dev/null
+++ b/vendor/console/scripts/wasmtime-wrapper.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $SCRIPT_DIR/..
+wasmtime run --env INSTA_WORKSPACE_ROOT=/ --mapdir "/::$(pwd)" -- "$@"
diff --git a/vendor/console/src/ansi.rs b/vendor/console/src/ansi.rs
new file mode 100644
index 0000000..3a3c96c
--- /dev/null
+++ b/vendor/console/src/ansi.rs
@@ -0,0 +1,438 @@
+use std::{
+ borrow::Cow,
+ iter::{FusedIterator, Peekable},
+ str::CharIndices,
+};
+
+#[derive(Debug, Clone, Copy)]
+enum State {
+ Start,
+ S1,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+ Trap,
+}
+
+impl Default for State {
+ fn default() -> Self {
+ Self::Start
+ }
+}
+
+impl State {
+ fn is_final(&self) -> bool {
+ #[allow(clippy::match_like_matches_macro)]
+ match self {
+ Self::S3 | Self::S5 | Self::S6 | Self::S7 | Self::S8 | Self::S9 | Self::S11 => true,
+ _ => false,
+ }
+ }
+
+ fn is_trapped(&self) -> bool {
+ #[allow(clippy::match_like_matches_macro)]
+ match self {
+ Self::Trap => true,
+ _ => false,
+ }
+ }
+
+ fn transition(&mut self, c: char) {
+ *self = match c {
+ '\u{1b}' | '\u{9b}' => match self {
+ Self::Start => Self::S1,
+ _ => Self::Trap,
+ },
+ '(' | ')' => match self {
+ Self::S1 => Self::S2,
+ Self::S2 | Self::S4 => Self::S4,
+ _ => Self::Trap,
+ },
+ ';' => match self {
+ Self::S1 | Self::S2 | Self::S4 => Self::S4,
+ Self::S5 | Self::S6 | Self::S7 | Self::S8 | Self::S10 => Self::S10,
+ _ => Self::Trap,
+ },
+
+ '[' | '#' | '?' => match self {
+ Self::S1 | Self::S2 | Self::S4 => Self::S4,
+ _ => Self::Trap,
+ },
+ '0'..='2' => match self {
+ Self::S1 | Self::S4 => Self::S5,
+ Self::S2 => Self::S3,
+ Self::S5 => Self::S6,
+ Self::S6 => Self::S7,
+ Self::S7 => Self::S8,
+ Self::S8 => Self::S9,
+ Self::S10 => Self::S5,
+ _ => Self::Trap,
+ },
+ '3'..='9' => match self {
+ Self::S1 | Self::S4 => Self::S5,
+ Self::S2 => Self::S5,
+ Self::S5 => Self::S6,
+ Self::S6 => Self::S7,
+ Self::S7 => Self::S8,
+ Self::S8 => Self::S9,
+ Self::S10 => Self::S5,
+ _ => Self::Trap,
+ },
+ 'A'..='P' | 'R' | 'Z' | 'c' | 'f'..='n' | 'q' | 'r' | 'y' | '=' | '>' | '<' => {
+ match self {
+ Self::S1
+ | Self::S2
+ | Self::S4
+ | Self::S5
+ | Self::S6
+ | Self::S7
+ | Self::S8
+ | Self::S10 => Self::S11,
+ _ => Self::Trap,
+ }
+ }
+ _ => Self::Trap,
+ };
+ }
+}
+
+#[derive(Debug)]
+struct Matches<'a> {
+ s: &'a str,
+ it: Peekable<CharIndices<'a>>,
+}
+
+impl<'a> Matches<'a> {
+ fn new(s: &'a str) -> Self {
+ let it = s.char_indices().peekable();
+ Self { s, it }
+ }
+}
+
+#[derive(Debug)]
+struct Match<'a> {
+ text: &'a str,
+ start: usize,
+ end: usize,
+}
+
+impl<'a> Match<'a> {
+ #[inline]
+ pub fn as_str(&self) -> &'a str {
+ &self.text[self.start..self.end]
+ }
+}
+
+impl<'a> Iterator for Matches<'a> {
+ type Item = Match<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ find_ansi_code_exclusive(&mut self.it).map(|(start, end)| Match {
+ text: self.s,
+ start,
+ end,
+ })
+ }
+}
+
+impl<'a> FusedIterator for Matches<'a> {}
+
+fn find_ansi_code_exclusive(it: &mut Peekable<CharIndices>) -> Option<(usize, usize)> {
+ 'outer: loop {
+ if let (start, '\u{1b}') | (start, '\u{9b}') = it.peek()? {
+ let start = *start;
+ let mut state = State::default();
+ let mut maybe_end = None;
+
+ loop {
+ let item = it.peek();
+
+ if let Some((idx, c)) = item {
+ state.transition(*c);
+
+ if state.is_final() {
+ maybe_end = Some(*idx);
+ }
+ }
+
+ // The match is greedy so run till we hit the trap state no matter what. A valid
+ // match is just one that was final at some point
+ if state.is_trapped() || item.is_none() {
+ match maybe_end {
+ Some(end) => {
+ // All possible final characters are a single byte so it's safe to make
+ // the end exclusive by just adding one
+ return Some((start, end + 1));
+ }
+ // The character we are peeking right now might be the start of a match so
+ // we want to continue the loop without popping off that char
+ None => continue 'outer,
+ }
+ }
+
+ it.next();
+ }
+ }
+
+ it.next();
+ }
+}
+
+/// Helper function to strip ansi codes.
+pub fn strip_ansi_codes(s: &str) -> Cow<str> {
+ let mut char_it = s.char_indices().peekable();
+ match find_ansi_code_exclusive(&mut char_it) {
+ Some(_) => {
+ let stripped: String = AnsiCodeIterator::new(s)
+ .filter_map(|(text, is_ansi)| if is_ansi { None } else { Some(text) })
+ .collect();
+ Cow::Owned(stripped)
+ }
+ None => Cow::Borrowed(s),
+ }
+}
+
+/// An iterator over ansi codes in a string.
+///
+/// This type can be used to scan over ansi codes in a string.
+/// It yields tuples in the form `(s, is_ansi)` where `s` is a slice of
+/// the original string and `is_ansi` indicates if the slice contains
+/// ansi codes or string values.
+pub struct AnsiCodeIterator<'a> {
+ s: &'a str,
+ pending_item: Option<(&'a str, bool)>,
+ last_idx: usize,
+ cur_idx: usize,
+ iter: Matches<'a>,
+}
+
+impl<'a> AnsiCodeIterator<'a> {
+ /// Creates a new ansi code iterator.
+ pub fn new(s: &'a str) -> AnsiCodeIterator<'a> {
+ AnsiCodeIterator {
+ s,
+ pending_item: None,
+ last_idx: 0,
+ cur_idx: 0,
+ iter: Matches::new(s),
+ }
+ }
+
+ /// Returns the string slice up to the current match.
+ pub fn current_slice(&self) -> &str {
+ &self.s[..self.cur_idx]
+ }
+
+ /// Returns the string slice from the current match to the end.
+ pub fn rest_slice(&self) -> &str {
+ &self.s[self.cur_idx..]
+ }
+}
+
+impl<'a> Iterator for AnsiCodeIterator<'a> {
+ type Item = (&'a str, bool);
+
+ fn next(&mut self) -> Option<(&'a str, bool)> {
+ if let Some(pending_item) = self.pending_item.take() {
+ self.cur_idx += pending_item.0.len();
+ Some(pending_item)
+ } else if let Some(m) = self.iter.next() {
+ let s = &self.s[self.last_idx..m.start];
+ self.last_idx = m.end;
+ if s.is_empty() {
+ self.cur_idx = m.end;
+ Some((m.as_str(), true))
+ } else {
+ self.cur_idx = m.start;
+ self.pending_item = Some((m.as_str(), true));
+ Some((s, false))
+ }
+ } else if self.last_idx < self.s.len() {
+ let rv = &self.s[self.last_idx..];
+ self.cur_idx = self.s.len();
+ self.last_idx = self.s.len();
+ Some((rv, false))
+ } else {
+ None
+ }
+ }
+}
+
+impl<'a> FusedIterator for AnsiCodeIterator<'a> {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use lazy_static::lazy_static;
+ use proptest::prelude::*;
+ use regex::Regex;
+
+ // The manual dfa `State` is a handwritten translation from the previously used regex. That
+ // regex is kept here and used to ensure that the new matches are the same as the old
+ lazy_static! {
+ static ref STRIP_ANSI_RE: Regex = Regex::new(
+ r"[\x1b\x9b]([()][012AB]|[\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><])",
+ )
+ .unwrap();
+ }
+
+ impl<'a, 'b> PartialEq<Match<'a>> for regex::Match<'b> {
+ fn eq(&self, other: &Match<'a>) -> bool {
+ self.start() == other.start && self.end() == other.end
+ }
+ }
+
+ proptest! {
+ #[test]
+ fn dfa_matches_old_regex(s in r"([\x1b\x9b]?.*){0,5}") {
+ let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect();
+ let new_matches: Vec<_> = Matches::new(&s).collect();
+ assert_eq!(old_matches, new_matches);
+ }
+ }
+
+ #[test]
+ fn dfa_matches_regex_on_small_strings() {
+ // To make sure the test runs in a reasonable time this is a slimmed down list of
+ // characters to reduce the groups that are only used with each other along with one
+ // arbitrarily chosen character not used in the regex (' ')
+ const POSSIBLE_BYTES: &[u8] = &[b' ', 0x1b, 0x9b, b'(', b'0', b'[', b';', b'3', b'C'];
+
+ fn check_all_strings_of_len(len: usize) {
+ _check_all_strings_of_len(len, &mut Vec::with_capacity(len));
+ }
+
+ fn _check_all_strings_of_len(len: usize, chunk: &mut Vec<u8>) {
+ if len == 0 {
+ if let Ok(s) = std::str::from_utf8(chunk) {
+ let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(s).collect();
+ let new_matches: Vec<_> = Matches::new(s).collect();
+ assert_eq!(old_matches, new_matches);
+ }
+
+ return;
+ }
+
+ for b in POSSIBLE_BYTES {
+ chunk.push(*b);
+ _check_all_strings_of_len(len - 1, chunk);
+ chunk.pop();
+ }
+ }
+
+ for str_len in 0..=6 {
+ check_all_strings_of_len(str_len);
+ }
+ }
+
+ #[test]
+ fn complex_data() {
+ let s = std::fs::read_to_string(
+ std::path::Path::new("tests")
+ .join("data")
+ .join("sample_zellij_session.log"),
+ )
+ .unwrap();
+
+ let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect();
+ let new_matches: Vec<_> = Matches::new(&s).collect();
+ assert_eq!(old_matches, new_matches);
+ }
+
+ #[test]
+ fn state_machine() {
+ let ansi_code = "\x1b)B";
+ let mut state = State::default();
+ assert!(!state.is_final());
+
+ for c in ansi_code.chars() {
+ state.transition(c);
+ }
+ assert!(state.is_final());
+
+ state.transition('A');
+ assert!(state.is_trapped());
+ }
+
+ #[test]
+ fn back_to_back_entry_char() {
+ let s = "\x1b\x1bf";
+ let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
+ assert_eq!(&["\x1bf"], matches.as_slice());
+ }
+
+ #[test]
+ fn early_paren_can_use_many_chars() {
+ let s = "\x1b(C";
+ let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
+ assert_eq!(&[s], matches.as_slice());
+ }
+
+ #[test]
+ fn long_run_of_digits() {
+ let s = "\u{1b}00000";
+ let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
+ assert_eq!(&[s], matches.as_slice());
+ }
+
+ #[test]
+ fn test_ansi_iter_re_vt100() {
+ let s = "\x1b(0lpq\x1b)Benglish";
+ let mut iter = AnsiCodeIterator::new(s);
+ assert_eq!(iter.next(), Some(("\x1b(0", true)));
+ assert_eq!(iter.next(), Some(("lpq", false)));
+ assert_eq!(iter.next(), Some(("\x1b)B", true)));
+ assert_eq!(iter.next(), Some(("english", false)));
+ }
+
+ #[test]
+ fn test_ansi_iter_re() {
+ use crate::style;
+ let s = format!("Hello {}!", style("World").red().force_styling(true));
+ let mut iter = AnsiCodeIterator::new(&s);
+ assert_eq!(iter.next(), Some(("Hello ", false)));
+ assert_eq!(iter.current_slice(), "Hello ");
+ assert_eq!(iter.rest_slice(), "\x1b[31mWorld\x1b[0m!");
+ assert_eq!(iter.next(), Some(("\x1b[31m", true)));
+ assert_eq!(iter.current_slice(), "Hello \x1b[31m");
+ assert_eq!(iter.rest_slice(), "World\x1b[0m!");
+ assert_eq!(iter.next(), Some(("World", false)));
+ assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld");
+ assert_eq!(iter.rest_slice(), "\x1b[0m!");
+ assert_eq!(iter.next(), Some(("\x1b[0m", true)));
+ assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld\x1b[0m");
+ assert_eq!(iter.rest_slice(), "!");
+ assert_eq!(iter.next(), Some(("!", false)));
+ assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld\x1b[0m!");
+ assert_eq!(iter.rest_slice(), "");
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_ansi_iter_re_on_multi() {
+ use crate::style;
+ let s = format!("{}", style("a").red().bold().force_styling(true));
+ let mut iter = AnsiCodeIterator::new(&s);
+ assert_eq!(iter.next(), Some(("\x1b[31m", true)));
+ assert_eq!(iter.current_slice(), "\x1b[31m");
+ assert_eq!(iter.rest_slice(), "\x1b[1ma\x1b[0m");
+ assert_eq!(iter.next(), Some(("\x1b[1m", true)));
+ assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1m");
+ assert_eq!(iter.rest_slice(), "a\x1b[0m");
+ assert_eq!(iter.next(), Some(("a", false)));
+ assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1ma");
+ assert_eq!(iter.rest_slice(), "\x1b[0m");
+ assert_eq!(iter.next(), Some(("\x1b[0m", true)));
+ assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1ma\x1b[0m");
+ assert_eq!(iter.rest_slice(), "");
+ assert_eq!(iter.next(), None);
+ }
+}
diff --git a/vendor/console/src/common_term.rs b/vendor/console/src/common_term.rs
new file mode 100644
index 0000000..020660a
--- /dev/null
+++ b/vendor/console/src/common_term.rs
@@ -0,0 +1,72 @@
+use std::io;
+
+use crate::term::Term;
+
+pub fn move_cursor_down(out: &Term, n: usize) -> io::Result<()> {
+ if n > 0 {
+ out.write_str(&format!("\x1b[{}B", n))
+ } else {
+ Ok(())
+ }
+}
+
+pub fn move_cursor_up(out: &Term, n: usize) -> io::Result<()> {
+ if n > 0 {
+ out.write_str(&format!("\x1b[{}A", n))
+ } else {
+ Ok(())
+ }
+}
+pub fn move_cursor_left(out: &Term, n: usize) -> io::Result<()> {
+ if n > 0 {
+ out.write_str(&format!("\x1b[{}D", n))
+ } else {
+ Ok(())
+ }
+}
+
+pub fn move_cursor_right(out: &Term, n: usize) -> io::Result<()> {
+ if n > 0 {
+ out.write_str(&format!("\x1b[{}C", n))
+ } else {
+ Ok(())
+ }
+}
+
+#[inline]
+pub fn move_cursor_to(out: &Term, x: usize, y: usize) -> io::Result<()> {
+ out.write_str(&format!("\x1B[{};{}H", y + 1, x + 1))
+}
+
+pub fn clear_chars(out: &Term, n: usize) -> io::Result<()> {
+ if n > 0 {
+ out.write_str(&format!("\x1b[{}D\x1b[0K", n))
+ } else {
+ Ok(())
+ }
+}
+
+#[inline]
+pub fn clear_line(out: &Term) -> io::Result<()> {
+ out.write_str("\r\x1b[2K")
+}
+
+#[inline]
+pub fn clear_screen(out: &Term) -> io::Result<()> {
+ out.write_str("\r\x1b[2J\r\x1b[H")
+}
+
+#[inline]
+pub fn clear_to_end_of_screen(out: &Term) -> io::Result<()> {
+ out.write_str("\r\x1b[0J")
+}
+
+#[inline]
+pub fn show_cursor(out: &Term) -> io::Result<()> {
+ out.write_str("\x1b[?25h")
+}
+
+#[inline]
+pub fn hide_cursor(out: &Term) -> io::Result<()> {
+ out.write_str("\x1b[?25l")
+}
diff --git a/vendor/console/src/kb.rs b/vendor/console/src/kb.rs
new file mode 100644
index 0000000..5258c13
--- /dev/null
+++ b/vendor/console/src/kb.rs
@@ -0,0 +1,29 @@
+/// Key mapping
+///
+/// This is an incomplete mapping of keys that are supported for reading
+/// from the keyboard.
+#[non_exhaustive]
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum Key {
+ Unknown,
+ /// Unrecognized sequence containing Esc and a list of chars
+ UnknownEscSeq(Vec<char>),
+ ArrowLeft,
+ ArrowRight,
+ ArrowUp,
+ ArrowDown,
+ Enter,
+ Escape,
+ Backspace,
+ Home,
+ End,
+ Tab,
+ BackTab,
+ Alt,
+ Del,
+ Shift,
+ Insert,
+ PageUp,
+ PageDown,
+ Char(char),
+}
diff --git a/vendor/console/src/lib.rs b/vendor/console/src/lib.rs
new file mode 100644
index 0000000..1b18afc
--- /dev/null
+++ b/vendor/console/src/lib.rs
@@ -0,0 +1,104 @@
+//! console is a library for Rust that provides access to various terminal
+//! features so you can build nicer looking command line interfaces. It
+//! comes with various tools and utilities for working with Terminals and
+//! formatting text.
+//!
+//! Best paired with other libraries in the family:
+//!
+//! * [dialoguer](https://docs.rs/dialoguer)
+//! * [indicatif](https://docs.rs/indicatif)
+//!
+//! # Terminal Access
+//!
+//! The terminal is abstracted through the `console::Term` type. It can
+//! either directly provide access to the connected terminal or by buffering
+//! up commands. A buffered terminal will however not be completely buffered
+//! on windows where cursor movements are currently directly passed through.
+//!
+//! Example usage:
+//!
+//! ```
+//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
+//! use std::thread;
+//! use std::time::Duration;
+//!
+//! use console::Term;
+//!
+//! let term = Term::stdout();
+//! term.write_line("Hello World!")?;
+//! thread::sleep(Duration::from_millis(2000));
+//! term.clear_line()?;
+//! # Ok(()) } test().unwrap();
+//! ```
+//!
+//! # Colors and Styles
+//!
+//! `console` automaticaly detects when to use colors based on the tty flag. It also
+//! provides higher level wrappers for styling text and other things that can be
+//! displayed with the `style` function and utility types.
+//!
+//! Example usage:
+//!
+//! ```
+//! use console::style;
+//!
+//! println!("This is {} neat", style("quite").cyan());
+//! ```
+//!
+//! You can also store styles and apply them to text later:
+//!
+//! ```
+//! use console::Style;
+//!
+//! let cyan = Style::new().cyan();
+//! println!("This is {} neat", cyan.apply_to("quite"));
+//! ```
+//!
+//! # Working with ANSI Codes
+//!
+//! The crate provids the function `strip_ansi_codes` to remove ANSI codes
+//! from a string as well as `measure_text_width` to calculate the width of a
+//! string as it would be displayed by the terminal. Both of those together
+//! are useful for more complex formatting.
+//!
+//! # Unicode Width Support
+//!
+//! By default this crate depends on the `unicode-width` crate to calculate
+//! the width of terminal characters. If you do not need this you can disable
+//! the `unicode-width` feature which will cut down on dependencies.
+//!
+//! # Features
+//!
+//! By default all features are enabled. The following features exist:
+//!
+//! * `unicode-width`: adds support for unicode width calculations
+//! * `ansi-parsing`: adds support for parsing ansi codes (this adds support
+//! for stripping and taking ansi escape codes into account for length
+//! calculations).
+
+pub use crate::kb::Key;
+pub use crate::term::{
+ user_attended, user_attended_stderr, Term, TermFamily, TermFeatures, TermTarget,
+};
+pub use crate::utils::{
+ colors_enabled, colors_enabled_stderr, measure_text_width, pad_str, pad_str_with,
+ set_colors_enabled, set_colors_enabled_stderr, style, truncate_str, Alignment, Attribute,
+ Color, Emoji, Style, StyledObject,
+};
+
+#[cfg(feature = "ansi-parsing")]
+pub use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
+
+mod common_term;
+mod kb;
+mod term;
+#[cfg(unix)]
+mod unix_term;
+mod utils;
+#[cfg(target_arch = "wasm32")]
+mod wasm_term;
+#[cfg(windows)]
+mod windows_term;
+
+#[cfg(feature = "ansi-parsing")]
+mod ansi;
diff --git a/vendor/console/src/term.rs b/vendor/console/src/term.rs
new file mode 100644
index 0000000..0a40258
--- /dev/null
+++ b/vendor/console/src/term.rs
@@ -0,0 +1,632 @@
+use std::fmt::{Debug, Display};
+use std::io::{self, Read, Write};
+use std::sync::{Arc, Mutex};
+
+#[cfg(unix)]
+use std::os::unix::io::{AsRawFd, RawFd};
+#[cfg(windows)]
+use std::os::windows::io::{AsRawHandle, RawHandle};
+
+use crate::{kb::Key, utils::Style};
+
+#[cfg(unix)]
+trait TermWrite: Write + Debug + AsRawFd + Send {}
+#[cfg(unix)]
+impl<T: Write + Debug + AsRawFd + Send> TermWrite for T {}
+
+#[cfg(unix)]
+trait TermRead: Read + Debug + AsRawFd + Send {}
+#[cfg(unix)]
+impl<T: Read + Debug + AsRawFd + Send> TermRead for T {}
+
+#[cfg(unix)]
+#[derive(Debug, Clone)]
+pub struct ReadWritePair {
+ #[allow(unused)]
+ read: Arc<Mutex<dyn TermRead>>,
+ write: Arc<Mutex<dyn TermWrite>>,
+ style: Style,
+}
+
+/// Where the term is writing.
+#[derive(Debug, Clone)]
+pub enum TermTarget {
+ Stdout,
+ Stderr,
+ #[cfg(unix)]
+ ReadWritePair(ReadWritePair),
+}
+
+#[derive(Debug)]
+pub struct TermInner {
+ target: TermTarget,
+ buffer: Option<Mutex<Vec<u8>>>,
+}
+
+/// The family of the terminal.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum TermFamily {
+ /// Redirected to a file or file like thing.
+ File,
+ /// A standard unix terminal.
+ UnixTerm,
+ /// A cmd.exe like windows console.
+ WindowsConsole,
+ /// A dummy terminal (for instance on wasm)
+ Dummy,
+}
+
+/// Gives access to the terminal features.
+#[derive(Debug, Clone)]
+pub struct TermFeatures<'a>(&'a Term);
+
+impl<'a> TermFeatures<'a> {
+ /// Check if this is a real user attended terminal (`isatty`)
+ #[inline]
+ pub fn is_attended(&self) -> bool {
+ is_a_terminal(self.0)
+ }
+
+ /// Check if colors are supported by this terminal.
+ ///
+ /// This does not check if colors are enabled. Currently all terminals
+ /// are considered to support colors
+ #[inline]
+ pub fn colors_supported(&self) -> bool {
+ is_a_color_terminal(self.0)
+ }
+
+ /// Check if this terminal is an msys terminal.
+ ///
+ /// This is sometimes useful to disable features that are known to not
+ /// work on msys terminals or require special handling.
+ #[inline]
+ pub fn is_msys_tty(&self) -> bool {
+ #[cfg(windows)]
+ {
+ msys_tty_on(self.0)
+ }
+ #[cfg(not(windows))]
+ {
+ false
+ }
+ }
+
+ /// Check if this terminal wants emojis.
+ #[inline]
+ pub fn wants_emoji(&self) -> bool {
+ self.is_attended() && wants_emoji()
+ }
+
+ /// Return the family of the terminal.
+ #[inline]
+ pub fn family(&self) -> TermFamily {
+ if !self.is_attended() {
+ return TermFamily::File;
+ }
+ #[cfg(windows)]
+ {
+ TermFamily::WindowsConsole
+ }
+ #[cfg(unix)]
+ {
+ TermFamily::UnixTerm
+ }
+ #[cfg(target_arch = "wasm32")]
+ {
+ TermFamily::Dummy
+ }
+ }
+}
+
+/// Abstraction around a terminal.
+///
+/// A terminal can be cloned. If a buffer is used it's shared across all
+/// clones which means it largely acts as a handle.
+#[derive(Clone, Debug)]
+pub struct Term {
+ inner: Arc<TermInner>,
+ pub(crate) is_msys_tty: bool,
+ pub(crate) is_tty: bool,
+}
+
+impl Term {
+ fn with_inner(inner: TermInner) -> Term {
+ let mut term = Term {
+ inner: Arc::new(inner),
+ is_msys_tty: false,
+ is_tty: false,
+ };
+
+ term.is_msys_tty = term.features().is_msys_tty();
+ term.is_tty = term.features().is_attended();
+ term
+ }
+
+ /// Return a new unbuffered terminal.
+ #[inline]
+ pub fn stdout() -> Term {
+ Term::with_inner(TermInner {
+ target: TermTarget::Stdout,
+ buffer: None,
+ })
+ }
+
+ /// Return a new unbuffered terminal to stderr.
+ #[inline]
+ pub fn stderr() -> Term {
+ Term::with_inner(TermInner {
+ target: TermTarget::Stderr,
+ buffer: None,
+ })
+ }
+
+ /// Return a new buffered terminal.
+ pub fn buffered_stdout() -> Term {
+ Term::with_inner(TermInner {
+ target: TermTarget::Stdout,
+ buffer: Some(Mutex::new(vec![])),
+ })
+ }
+
+ /// Return a new buffered terminal to stderr.
+ pub fn buffered_stderr() -> Term {
+ Term::with_inner(TermInner {
+ target: TermTarget::Stderr,
+ buffer: Some(Mutex::new(vec![])),
+ })
+ }
+
+ /// Return a terminal for the given Read/Write pair styled like stderr.
+ #[cfg(unix)]
+ pub fn read_write_pair<R, W>(read: R, write: W) -> Term
+ where
+ R: Read + Debug + AsRawFd + Send + 'static,
+ W: Write + Debug + AsRawFd + Send + 'static,
+ {
+ Self::read_write_pair_with_style(read, write, Style::new().for_stderr())
+ }
+
+ /// Return a terminal for the given Read/Write pair.
+ #[cfg(unix)]
+ pub fn read_write_pair_with_style<R, W>(read: R, write: W, style: Style) -> Term
+ where
+ R: Read + Debug + AsRawFd + Send + 'static,
+ W: Write + Debug + AsRawFd + Send + 'static,
+ {
+ Term::with_inner(TermInner {
+ target: TermTarget::ReadWritePair(ReadWritePair {
+ read: Arc::new(Mutex::new(read)),
+ write: Arc::new(Mutex::new(write)),
+ style,
+ }),
+ buffer: None,
+ })
+ }
+
+ /// Return the style for this terminal.
+ #[inline]
+ pub fn style(&self) -> Style {
+ match self.inner.target {
+ TermTarget::Stderr => Style::new().for_stderr(),
+ TermTarget::Stdout => Style::new().for_stdout(),
+ #[cfg(unix)]
+ TermTarget::ReadWritePair(ReadWritePair { ref style, .. }) => style.clone(),
+ }
+ }
+
+ /// Return the target of this terminal.
+ #[inline]
+ pub fn target(&self) -> TermTarget {
+ self.inner.target.clone()
+ }
+
+ #[doc(hidden)]
+ pub fn write_str(&self, s: &str) -> io::Result<()> {
+ match self.inner.buffer {
+ Some(ref buffer) => buffer.lock().unwrap().write_all(s.as_bytes()),
+ None => self.write_through(s.as_bytes()),
+ }
+ }
+
+ /// Write a string to the terminal and add a newline.
+ pub fn write_line(&self, s: &str) -> io::Result<()> {
+ match self.inner.buffer {
+ Some(ref mutex) => {
+ let mut buffer = mutex.lock().unwrap();
+ buffer.extend_from_slice(s.as_bytes());
+ buffer.push(b'\n');
+ Ok(())
+ }
+ None => self.write_through(format!("{}\n", s).as_bytes()),
+ }
+ }
+
+ /// Read a single character from the terminal.
+ ///
+ /// This does not echo the character and blocks until a single character
+ /// or complete key chord is entered. If the terminal is not user attended
+ /// the return value will be an error.
+ pub fn read_char(&self) -> io::Result<char> {
+ if !self.is_tty {
+ return Err(io::Error::new(
+ io::ErrorKind::NotConnected,
+ "Not a terminal",
+ ));
+ }
+ loop {
+ match self.read_key()? {
+ Key::Char(c) => {
+ return Ok(c);
+ }
+ Key::Enter => {
+ return Ok('\n');
+ }
+ _ => {}
+ }
+ }
+ }
+
+ /// Read a single key form the terminal.
+ ///
+ /// This does not echo anything. If the terminal is not user attended
+ /// the return value will always be the unknown key.
+ pub fn read_key(&self) -> io::Result<Key> {
+ if !self.is_tty {
+ Ok(Key::Unknown)
+ } else {
+ read_single_key()
+ }
+ }
+
+ /// Read one line of input.
+ ///
+ /// This does not include the trailing newline. If the terminal is not
+ /// user attended the return value will always be an empty string.
+ pub fn read_line(&self) -> io::Result<String> {
+ if !self.is_tty {
+ return Ok("".into());
+ }
+ let mut rv = String::new();
+ io::stdin().read_line(&mut rv)?;
+ let len = rv.trim_end_matches(&['\r', '\n'][..]).len();
+ rv.truncate(len);
+ Ok(rv)
+ }
+
+ /// Read one line of input with initial text.
+ ///
+ /// This does not include the trailing newline. If the terminal is not
+ /// user attended the return value will always be an empty string.
+ pub fn read_line_initial_text(&self, initial: &str) -> io::Result<String> {
+ if !self.is_tty {
+ return Ok("".into());
+ }
+ self.write_str(initial)?;
+
+ let mut chars: Vec<char> = initial.chars().collect();
+
+ loop {
+ match self.read_key()? {
+ Key::Backspace => {
+ if chars.pop().is_some() {
+ self.clear_chars(1)?;
+ }
+ self.flush()?;
+ }
+ Key::Char(chr) => {
+ chars.push(chr);
+ let mut bytes_char = [0; 4];
+ chr.encode_utf8(&mut bytes_char);
+ self.write_str(chr.encode_utf8(&mut bytes_char))?;
+ self.flush()?;
+ }
+ Key::Enter => {
+ self.write_line("")?;
+ break;
+ }
+ _ => (),
+ }
+ }
+ Ok(chars.iter().collect::<String>())
+ }
+
+ /// Read a line of input securely.
+ ///
+ /// This is similar to `read_line` but will not echo the output. This
+ /// also switches the terminal into a different mode where not all
+ /// characters might be accepted.
+ pub fn read_secure_line(&self) -> io::Result<String> {
+ if !self.is_tty {
+ return Ok("".into());
+ }
+ match read_secure() {
+ Ok(rv) => {
+ self.write_line("")?;
+ Ok(rv)
+ }
+ Err(err) => Err(err),
+ }
+ }
+
+ /// Flush internal buffers.
+ ///
+ /// This forces the contents of the internal buffer to be written to
+ /// the terminal. This is unnecessary for unbuffered terminals which
+ /// will automatically flush.
+ pub fn flush(&self) -> io::Result<()> {
+ if let Some(ref buffer) = self.inner.buffer {
+ let mut buffer = buffer.lock().unwrap();
+ if !buffer.is_empty() {
+ self.write_through(&buffer[..])?;
+ buffer.clear();
+ }
+ }
+ Ok(())
+ }
+
+ /// Check if the terminal is indeed a terminal.
+ #[inline]
+ pub fn is_term(&self) -> bool {
+ self.is_tty
+ }
+
+ /// Check for common terminal features.
+ #[inline]
+ pub fn features(&self) -> TermFeatures<'_> {
+ TermFeatures(self)
+ }
+
+ /// Return the terminal size in rows and columns or gets sensible defaults.
+ #[inline]
+ pub fn size(&self) -> (u16, u16) {
+ self.size_checked().unwrap_or((24, DEFAULT_WIDTH))
+ }
+
+ /// Return the terminal size in rows and columns.
+ ///
+ /// If the size cannot be reliably determined `None` is returned.
+ #[inline]
+ pub fn size_checked(&self) -> Option<(u16, u16)> {
+ terminal_size(self)
+ }
+
+ /// Move the cursor to row `x` and column `y`. Values are 0-based.
+ #[inline]
+ pub fn move_cursor_to(&self, x: usize, y: usize) -> io::Result<()> {
+ move_cursor_to(self, x, y)
+ }
+
+ /// Move the cursor up by `n` lines, if possible.
+ ///
+ /// If there are less than `n` lines above the current cursor position,
+ /// the cursor is moved to the top line of the terminal (i.e., as far up as possible).
+ #[inline]
+ pub fn move_cursor_up(&self, n: usize) -> io::Result<()> {
+ move_cursor_up(self, n)
+ }
+
+ /// Move the cursor down by `n` lines, if possible.
+ ///
+ /// If there are less than `n` lines below the current cursor position,
+ /// the cursor is moved to the bottom line of the terminal (i.e., as far down as possible).
+ #[inline]
+ pub fn move_cursor_down(&self, n: usize) -> io::Result<()> {
+ move_cursor_down(self, n)
+ }
+
+ /// Move the cursor `n` characters to the left, if possible.
+ ///
+ /// If there are fewer than `n` characters to the left of the current cursor position,
+ /// the cursor is moved to the beginning of the line (i.e., as far to the left as possible).
+ #[inline]
+ pub fn move_cursor_left(&self, n: usize) -> io::Result<()> {
+ move_cursor_left(self, n)
+ }
+
+ /// Move the cursor `n` characters to the right.
+ ///
+ /// If there are fewer than `n` characters to the right of the current cursor position,
+ /// the cursor is moved to the end of the current line (i.e., as far to the right as possible).
+ #[inline]
+ pub fn move_cursor_right(&self, n: usize) -> io::Result<()> {
+ move_cursor_right(self, n)
+ }
+
+ /// Clear the current line.
+ ///
+ /// Position the cursor at the beginning of the current line.
+ #[inline]
+ pub fn clear_line(&self) -> io::Result<()> {
+ clear_line(self)
+ }
+
+ /// Clear the last `n` lines before the current line.
+ ///
+ /// Position the cursor at the beginning of the first line that was cleared.
+ pub fn clear_last_lines(&self, n: usize) -> io::Result<()> {
+ self.move_cursor_up(n)?;
+ for _ in 0..n {
+ self.clear_line()?;
+ self.move_cursor_down(1)?;
+ }
+ self.move_cursor_up(n)?;
+ Ok(())
+ }
+
+ /// Clear the entire screen.
+ ///
+ /// Move the cursor to the upper left corner of the screen.
+ #[inline]
+ pub fn clear_screen(&self) -> io::Result<()> {
+ clear_screen(self)
+ }
+
+ /// Clear everything from the current cursor position to the end of the screen.
+ /// The cursor stays in its position.
+ #[inline]
+ pub fn clear_to_end_of_screen(&self) -> io::Result<()> {
+ clear_to_end_of_screen(self)
+ }
+
+ /// Clear the last `n` characters of the current line.
+ #[inline]
+ pub fn clear_chars(&self, n: usize) -> io::Result<()> {
+ clear_chars(self, n)
+ }
+
+ /// Set the terminal title.
+ pub fn set_title<T: Display>(&self, title: T) {
+ if !self.is_tty {
+ return;
+ }
+ set_title(title);
+ }
+
+ /// Make the cursor visible again.
+ #[inline]
+ pub fn show_cursor(&self) -> io::Result<()> {
+ show_cursor(self)
+ }
+
+ /// Hide the cursor.
+ #[inline]
+ pub fn hide_cursor(&self) -> io::Result<()> {
+ hide_cursor(self)
+ }
+
+ // helpers
+
+ #[cfg(all(windows, feature = "windows-console-colors"))]
+ fn write_through(&self, bytes: &[u8]) -> io::Result<()> {
+ if self.is_msys_tty || !self.is_tty {
+ self.write_through_common(bytes)
+ } else {
+ match self.inner.target {
+ TermTarget::Stdout => console_colors(self, Console::stdout()?, bytes),
+ TermTarget::Stderr => console_colors(self, Console::stderr()?, bytes),
+ }
+ }
+ }
+
+ #[cfg(not(all(windows, feature = "windows-console-colors")))]
+ fn write_through(&self, bytes: &[u8]) -> io::Result<()> {
+ self.write_through_common(bytes)
+ }
+
+ pub(crate) fn write_through_common(&self, bytes: &[u8]) -> io::Result<()> {
+ match self.inner.target {
+ TermTarget::Stdout => {
+ io::stdout().write_all(bytes)?;
+ io::stdout().flush()?;
+ }
+ TermTarget::Stderr => {
+ io::stderr().write_all(bytes)?;
+ io::stderr().flush()?;
+ }
+ #[cfg(unix)]
+ TermTarget::ReadWritePair(ReadWritePair { ref write, .. }) => {
+ let mut write = write.lock().unwrap();
+ write.write_all(bytes)?;
+ write.flush()?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// A fast way to check if the application has a user attended for stdout.
+///
+/// This means that stdout is connected to a terminal instead of a
+/// file or redirected by other means. This is a shortcut for
+/// checking the `is_attended` feature on the stdout terminal.
+#[inline]
+pub fn user_attended() -> bool {
+ Term::stdout().features().is_attended()
+}
+
+/// A fast way to check if the application has a user attended for stderr.
+///
+/// This means that stderr is connected to a terminal instead of a
+/// file or redirected by other means. This is a shortcut for
+/// checking the `is_attended` feature on the stderr terminal.
+#[inline]
+pub fn user_attended_stderr() -> bool {
+ Term::stderr().features().is_attended()
+}
+
+#[cfg(unix)]
+impl AsRawFd for Term {
+ fn as_raw_fd(&self) -> RawFd {
+ match self.inner.target {
+ TermTarget::Stdout => libc::STDOUT_FILENO,
+ TermTarget::Stderr => libc::STDERR_FILENO,
+ TermTarget::ReadWritePair(ReadWritePair { ref write, .. }) => {
+ write.lock().unwrap().as_raw_fd()
+ }
+ }
+ }
+}
+
+#[cfg(windows)]
+impl AsRawHandle for Term {
+ fn as_raw_handle(&self) -> RawHandle {
+ use windows_sys::Win32::System::Console::{
+ GetStdHandle, STD_ERROR_HANDLE, STD_OUTPUT_HANDLE,
+ };
+
+ unsafe {
+ GetStdHandle(match self.inner.target {
+ TermTarget::Stdout => STD_OUTPUT_HANDLE,
+ TermTarget::Stderr => STD_ERROR_HANDLE,
+ }) as RawHandle
+ }
+ }
+}
+
+impl Write for Term {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match self.inner.buffer {
+ Some(ref buffer) => buffer.lock().unwrap().write_all(buf),
+ None => self.write_through(buf),
+ }?;
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Term::flush(self)
+ }
+}
+
+impl<'a> Write for &'a Term {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match self.inner.buffer {
+ Some(ref buffer) => buffer.lock().unwrap().write_all(buf),
+ None => self.write_through(buf),
+ }?;
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Term::flush(self)
+ }
+}
+
+impl Read for Term {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ io::stdin().read(buf)
+ }
+}
+
+impl<'a> Read for &'a Term {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ io::stdin().read(buf)
+ }
+}
+
+#[cfg(unix)]
+pub use crate::unix_term::*;
+#[cfg(target_arch = "wasm32")]
+pub use crate::wasm_term::*;
+#[cfg(windows)]
+pub use crate::windows_term::*;
diff --git a/vendor/console/src/unix_term.rs b/vendor/console/src/unix_term.rs
new file mode 100644
index 0000000..8e1e592
--- /dev/null
+++ b/vendor/console/src/unix_term.rs
@@ -0,0 +1,362 @@
+use std::env;
+use std::fmt::Display;
+use std::fs;
+use std::io;
+use std::io::{BufRead, BufReader};
+use std::mem;
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+use std::str;
+
+use crate::kb::Key;
+use crate::term::Term;
+
+pub use crate::common_term::*;
+
+pub const DEFAULT_WIDTH: u16 = 80;
+
+#[inline]
+pub fn is_a_terminal(out: &Term) -> bool {
+ unsafe { libc::isatty(out.as_raw_fd()) != 0 }
+}
+
+pub fn is_a_color_terminal(out: &Term) -> bool {
+ if !is_a_terminal(out) {
+ return false;
+ }
+
+ if env::var("NO_COLOR").is_ok() {
+ return false;
+ }
+
+ match env::var("TERM") {
+ Ok(term) => term != "dumb",
+ Err(_) => false,
+ }
+}
+
+pub fn c_result<F: FnOnce() -> libc::c_int>(f: F) -> io::Result<()> {
+ let res = f();
+ if res != 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+}
+
+pub fn terminal_size(out: &Term) -> Option<(u16, u16)> {
+ unsafe {
+ if libc::isatty(libc::STDOUT_FILENO) != 1 {
+ return None;
+ }
+
+ let mut winsize: libc::winsize = std::mem::zeroed();
+
+ // FIXME: ".into()" used as a temporary fix for a libc bug
+ // https://github.com/rust-lang/libc/pull/704
+ #[allow(clippy::useless_conversion)]
+ libc::ioctl(out.as_raw_fd(), libc::TIOCGWINSZ.into(), &mut winsize);
+ if winsize.ws_row > 0 && winsize.ws_col > 0 {
+ Some((winsize.ws_row as u16, winsize.ws_col as u16))
+ } else {
+ None
+ }
+ }
+}
+
+pub fn read_secure() -> io::Result<String> {
+ let f_tty;
+ let fd = unsafe {
+ if libc::isatty(libc::STDIN_FILENO) == 1 {
+ f_tty = None;
+ libc::STDIN_FILENO
+ } else {
+ let f = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open("/dev/tty")?;
+ let fd = f.as_raw_fd();
+ f_tty = Some(BufReader::new(f));
+ fd
+ }
+ };
+
+ let mut termios = core::mem::MaybeUninit::uninit();
+ c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
+ let mut termios = unsafe { termios.assume_init() };
+ let original = termios;
+ termios.c_lflag &= !libc::ECHO;
+ c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSAFLUSH, &termios) })?;
+ let mut rv = String::new();
+
+ let read_rv = if let Some(mut f) = f_tty {
+ f.read_line(&mut rv)
+ } else {
+ io::stdin().read_line(&mut rv)
+ };
+
+ c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSAFLUSH, &original) })?;
+
+ read_rv.map(|_| {
+ let len = rv.trim_end_matches(&['\r', '\n'][..]).len();
+ rv.truncate(len);
+ rv
+ })
+}
+
+fn poll_fd(fd: i32, timeout: i32) -> io::Result<bool> {
+ let mut pollfd = libc::pollfd {
+ fd,
+ events: libc::POLLIN,
+ revents: 0,
+ };
+ let ret = unsafe { libc::poll(&mut pollfd as *mut _, 1, timeout) };
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(pollfd.revents & libc::POLLIN != 0)
+ }
+}
+
+#[cfg(target_os = "macos")]
+fn select_fd(fd: i32, timeout: i32) -> io::Result<bool> {
+ unsafe {
+ let mut read_fd_set: libc::fd_set = mem::zeroed();
+
+ let mut timeout_val;
+ let timeout = if timeout < 0 {
+ ptr::null_mut()
+ } else {
+ timeout_val = libc::timeval {
+ tv_sec: (timeout / 1000) as _,
+ tv_usec: (timeout * 1000) as _,
+ };
+ &mut timeout_val
+ };
+
+ libc::FD_ZERO(&mut read_fd_set);
+ libc::FD_SET(fd, &mut read_fd_set);
+ let ret = libc::select(
+ fd + 1,
+ &mut read_fd_set,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ timeout,
+ );
+ if ret < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(libc::FD_ISSET(fd, &read_fd_set))
+ }
+ }
+}
+
+fn select_or_poll_term_fd(fd: i32, timeout: i32) -> io::Result<bool> {
+ // There is a bug on macos that ttys cannot be polled, only select()
+ // works. However given how problematic select is in general, we
+ // normally want to use poll there too.
+ #[cfg(target_os = "macos")]
+ {
+ if unsafe { libc::isatty(fd) == 1 } {
+ return select_fd(fd, timeout);
+ }
+ }
+ poll_fd(fd, timeout)
+}
+
+fn read_single_char(fd: i32) -> io::Result<Option<char>> {
+ // timeout of zero means that it will not block
+ let is_ready = select_or_poll_term_fd(fd, 0)?;
+
+ if is_ready {
+ // if there is something to be read, take 1 byte from it
+ let mut buf: [u8; 1] = [0];
+
+ read_bytes(fd, &mut buf, 1)?;
+ Ok(Some(buf[0] as char))
+ } else {
+ //there is nothing to be read
+ Ok(None)
+ }
+}
+
+// Similar to libc::read. Read count bytes into slice buf from descriptor fd.
+// If successful, return the number of bytes read.
+// Will return an error if nothing was read, i.e when called at end of file.
+fn read_bytes(fd: i32, buf: &mut [u8], count: u8) -> io::Result<u8> {
+ let read = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, count as usize) };
+ if read < 0 {
+ Err(io::Error::last_os_error())
+ } else if read == 0 {
+ Err(io::Error::new(
+ io::ErrorKind::UnexpectedEof,
+ "Reached end of file",
+ ))
+ } else if buf[0] == b'\x03' {
+ Err(io::Error::new(
+ io::ErrorKind::Interrupted,
+ "read interrupted",
+ ))
+ } else {
+ Ok(read as u8)
+ }
+}
+
+fn read_single_key_impl(fd: i32) -> Result<Key, io::Error> {
+ loop {
+ match read_single_char(fd)? {
+ Some('\x1b') => {
+ // Escape was read, keep reading in case we find a familiar key
+ break if let Some(c1) = read_single_char(fd)? {
+ if c1 == '[' {
+ if let Some(c2) = read_single_char(fd)? {
+ match c2 {
+ 'A' => Ok(Key::ArrowUp),
+ 'B' => Ok(Key::ArrowDown),
+ 'C' => Ok(Key::ArrowRight),
+ 'D' => Ok(Key::ArrowLeft),
+ 'H' => Ok(Key::Home),
+ 'F' => Ok(Key::End),
+ 'Z' => Ok(Key::BackTab),
+ _ => {
+ let c3 = read_single_char(fd)?;
+ if let Some(c3) = c3 {
+ if c3 == '~' {
+ match c2 {
+ '1' => Ok(Key::Home), // tmux
+ '2' => Ok(Key::Insert),
+ '3' => Ok(Key::Del),
+ '4' => Ok(Key::End), // tmux
+ '5' => Ok(Key::PageUp),
+ '6' => Ok(Key::PageDown),
+ '7' => Ok(Key::Home), // xrvt
+ '8' => Ok(Key::End), // xrvt
+ _ => Ok(Key::UnknownEscSeq(vec![c1, c2, c3])),
+ }
+ } else {
+ Ok(Key::UnknownEscSeq(vec![c1, c2, c3]))
+ }
+ } else {
+ // \x1b[ and 1 more char
+ Ok(Key::UnknownEscSeq(vec![c1, c2]))
+ }
+ }
+ }
+ } else {
+ // \x1b[ and no more input
+ Ok(Key::UnknownEscSeq(vec![c1]))
+ }
+ } else {
+ // char after escape is not [
+ Ok(Key::UnknownEscSeq(vec![c1]))
+ }
+ } else {
+ //nothing after escape
+ Ok(Key::Escape)
+ };
+ }
+ Some(c) => {
+ let byte = c as u8;
+ let mut buf: [u8; 4] = [byte, 0, 0, 0];
+
+ break if byte & 224u8 == 192u8 {
+ // a two byte unicode character
+ read_bytes(fd, &mut buf[1..], 1)?;
+ Ok(key_from_utf8(&buf[..2]))
+ } else if byte & 240u8 == 224u8 {
+ // a three byte unicode character
+ read_bytes(fd, &mut buf[1..], 2)?;
+ Ok(key_from_utf8(&buf[..3]))
+ } else if byte & 248u8 == 240u8 {
+ // a four byte unicode character
+ read_bytes(fd, &mut buf[1..], 3)?;
+ Ok(key_from_utf8(&buf[..4]))
+ } else {
+ Ok(match c {
+ '\n' | '\r' => Key::Enter,
+ '\x7f' => Key::Backspace,
+ '\t' => Key::Tab,
+ '\x01' => Key::Home, // Control-A (home)
+ '\x05' => Key::End, // Control-E (end)
+ '\x08' => Key::Backspace, // Control-H (8) (Identical to '\b')
+ _ => Key::Char(c),
+ })
+ };
+ }
+ None => {
+ // there is no subsequent byte ready to be read, block and wait for input
+ // negative timeout means that it will block indefinitely
+ match select_or_poll_term_fd(fd, -1) {
+ Ok(_) => continue,
+ Err(_) => break Err(io::Error::last_os_error()),
+ }
+ }
+ }
+ }
+}
+
+pub fn read_single_key() -> io::Result<Key> {
+ let tty_f;
+ let fd = unsafe {
+ if libc::isatty(libc::STDIN_FILENO) == 1 {
+ libc::STDIN_FILENO
+ } else {
+ tty_f = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open("/dev/tty")?;
+ tty_f.as_raw_fd()
+ }
+ };
+ let mut termios = core::mem::MaybeUninit::uninit();
+ c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
+ let mut termios = unsafe { termios.assume_init() };
+ let original = termios;
+ unsafe { libc::cfmakeraw(&mut termios) };
+ termios.c_oflag = original.c_oflag;
+ c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &termios) })?;
+ let rv: io::Result<Key> = read_single_key_impl(fd);
+ c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &original) })?;
+
+ // if the user hit ^C we want to signal SIGINT to outselves.
+ if let Err(ref err) = rv {
+ if err.kind() == io::ErrorKind::Interrupted {
+ unsafe {
+ libc::raise(libc::SIGINT);
+ }
+ }
+ }
+
+ rv
+}
+
+pub fn key_from_utf8(buf: &[u8]) -> Key {
+ if let Ok(s) = str::from_utf8(buf) {
+ if let Some(c) = s.chars().next() {
+ return Key::Char(c);
+ }
+ }
+ Key::Unknown
+}
+
+#[cfg(not(target_os = "macos"))]
+lazy_static::lazy_static! {
+ static ref IS_LANG_UTF8: bool = match std::env::var("LANG") {
+ Ok(lang) => lang.to_uppercase().ends_with("UTF-8"),
+ _ => false,
+ };
+}
+
+#[cfg(target_os = "macos")]
+pub fn wants_emoji() -> bool {
+ true
+}
+
+#[cfg(not(target_os = "macos"))]
+pub fn wants_emoji() -> bool {
+ *IS_LANG_UTF8
+}
+
+pub fn set_title<T: Display>(title: T) {
+ print!("\x1b]0;{}\x07", title);
+}
diff --git a/vendor/console/src/utils.rs b/vendor/console/src/utils.rs
new file mode 100644
index 0000000..9e6b942
--- /dev/null
+++ b/vendor/console/src/utils.rs
@@ -0,0 +1,962 @@
+use std::borrow::Cow;
+use std::collections::BTreeSet;
+use std::env;
+use std::fmt;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use lazy_static::lazy_static;
+
+use crate::term::{wants_emoji, Term};
+
+#[cfg(feature = "ansi-parsing")]
+use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
+
+#[cfg(not(feature = "ansi-parsing"))]
+fn strip_ansi_codes(s: &str) -> &str {
+ s
+}
+
+fn default_colors_enabled(out: &Term) -> bool {
+ (out.features().colors_supported()
+ && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
+ || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
+}
+
+lazy_static! {
+ static ref STDOUT_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stdout()));
+ static ref STDERR_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stderr()));
+}
+
+/// Returns `true` if colors should be enabled for stdout.
+///
+/// This honors the [clicolors spec](http://bixense.com/clicolors/).
+///
+/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
+/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
+/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
+#[inline]
+pub fn colors_enabled() -> bool {
+ STDOUT_COLORS.load(Ordering::Relaxed)
+}
+
+/// Forces colorization on or off for stdout.
+///
+/// This overrides the default for the current process and changes the return value of the
+/// `colors_enabled` function.
+#[inline]
+pub fn set_colors_enabled(val: bool) {
+ STDOUT_COLORS.store(val, Ordering::Relaxed)
+}
+
+/// Returns `true` if colors should be enabled for stderr.
+///
+/// This honors the [clicolors spec](http://bixense.com/clicolors/).
+///
+/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
+/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
+/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
+#[inline]
+pub fn colors_enabled_stderr() -> bool {
+ STDERR_COLORS.load(Ordering::Relaxed)
+}
+
+/// Forces colorization on or off for stderr.
+///
+/// This overrides the default for the current process and changes the return value of the
+/// `colors_enabled` function.
+#[inline]
+pub fn set_colors_enabled_stderr(val: bool) {
+ STDERR_COLORS.store(val, Ordering::Relaxed)
+}
+
+/// Measure the width of a string in terminal characters.
+pub fn measure_text_width(s: &str) -> usize {
+ str_width(&strip_ansi_codes(s))
+}
+
+/// A terminal color.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Color {
+ Black,
+ Red,
+ Green,
+ Yellow,
+ Blue,
+ Magenta,
+ Cyan,
+ White,
+ Color256(u8),
+}
+
+impl Color {
+ #[inline]
+ fn ansi_num(self) -> usize {
+ match self {
+ Color::Black => 0,
+ Color::Red => 1,
+ Color::Green => 2,
+ Color::Yellow => 3,
+ Color::Blue => 4,
+ Color::Magenta => 5,
+ Color::Cyan => 6,
+ Color::White => 7,
+ Color::Color256(x) => x as usize,
+ }
+ }
+
+ #[inline]
+ fn is_color256(self) -> bool {
+ #[allow(clippy::match_like_matches_macro)]
+ match self {
+ Color::Color256(_) => true,
+ _ => false,
+ }
+ }
+}
+
+/// A terminal style attribute.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
+pub enum Attribute {
+ Bold,
+ Dim,
+ Italic,
+ Underlined,
+ Blink,
+ BlinkFast,
+ Reverse,
+ Hidden,
+ StrikeThrough,
+}
+
+impl Attribute {
+ #[inline]
+ fn ansi_num(self) -> usize {
+ match self {
+ Attribute::Bold => 1,
+ Attribute::Dim => 2,
+ Attribute::Italic => 3,
+ Attribute::Underlined => 4,
+ Attribute::Blink => 5,
+ Attribute::BlinkFast => 6,
+ Attribute::Reverse => 7,
+ Attribute::Hidden => 8,
+ Attribute::StrikeThrough => 9,
+ }
+ }
+}
+
+/// Defines the alignment for padding operations.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Alignment {
+ Left,
+ Center,
+ Right,
+}
+
+/// A stored style that can be applied.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Style {
+ fg: Option<Color>,
+ bg: Option<Color>,
+ fg_bright: bool,
+ bg_bright: bool,
+ attrs: BTreeSet<Attribute>,
+ force: Option<bool>,
+ for_stderr: bool,
+}
+
+impl Default for Style {
+ fn default() -> Style {
+ Style::new()
+ }
+}
+
+impl Style {
+ /// Returns an empty default style.
+ pub fn new() -> Style {
+ Style {
+ fg: None,
+ bg: None,
+ fg_bright: false,
+ bg_bright: false,
+ attrs: BTreeSet::new(),
+ force: None,
+ for_stderr: false,
+ }
+ }
+
+ /// Creates a style from a dotted string.
+ ///
+ /// Effectively the string is split at each dot and then the
+ /// terms in between are applied. For instance `red.on_blue` will
+ /// create a string that is red on blue background. `9.on_12` is
+ /// the same, but using 256 color numbers. Unknown terms are
+ /// ignored.
+ pub fn from_dotted_str(s: &str) -> Style {
+ let mut rv = Style::new();
+ for part in s.split('.') {
+ rv = match part {
+ "black" => rv.black(),
+ "red" => rv.red(),
+ "green" => rv.green(),
+ "yellow" => rv.yellow(),
+ "blue" => rv.blue(),
+ "magenta" => rv.magenta(),
+ "cyan" => rv.cyan(),
+ "white" => rv.white(),
+ "bright" => rv.bright(),
+ "on_black" => rv.on_black(),
+ "on_red" => rv.on_red(),
+ "on_green" => rv.on_green(),
+ "on_yellow" => rv.on_yellow(),
+ "on_blue" => rv.on_blue(),
+ "on_magenta" => rv.on_magenta(),
+ "on_cyan" => rv.on_cyan(),
+ "on_white" => rv.on_white(),
+ "on_bright" => rv.on_bright(),
+ "bold" => rv.bold(),
+ "dim" => rv.dim(),
+ "underlined" => rv.underlined(),
+ "blink" => rv.blink(),
+ "blink_fast" => rv.blink_fast(),
+ "reverse" => rv.reverse(),
+ "hidden" => rv.hidden(),
+ "strikethrough" => rv.strikethrough(),
+ on_c if on_c.starts_with("on_") => {
+ if let Ok(n) = on_c[3..].parse::<u8>() {
+ rv.on_color256(n)
+ } else {
+ continue;
+ }
+ }
+ c => {
+ if let Ok(n) = c.parse::<u8>() {
+ rv.color256(n)
+ } else {
+ continue;
+ }
+ }
+ };
+ }
+ rv
+ }
+
+ /// Apply the style to something that can be displayed.
+ pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
+ StyledObject {
+ style: self.clone(),
+ val,
+ }
+ }
+
+ /// Forces styling on or off.
+ ///
+ /// This overrides the automatic detection.
+ #[inline]
+ pub fn force_styling(mut self, value: bool) -> Style {
+ self.force = Some(value);
+ self
+ }
+
+ /// Specifies that style is applying to something being written on stderr.
+ #[inline]
+ pub fn for_stderr(mut self) -> Style {
+ self.for_stderr = true;
+ self
+ }
+
+ /// Specifies that style is applying to something being written on stdout.
+ ///
+ /// This is the default behaviour.
+ #[inline]
+ pub fn for_stdout(mut self) -> Style {
+ self.for_stderr = false;
+ self
+ }
+
+ /// Sets a foreground color.
+ #[inline]
+ pub fn fg(mut self, color: Color) -> Style {
+ self.fg = Some(color);
+ self
+ }
+
+ /// Sets a background color.
+ #[inline]
+ pub fn bg(mut self, color: Color) -> Style {
+ self.bg = Some(color);
+ self
+ }
+
+ /// Adds a attr.
+ #[inline]
+ pub fn attr(mut self, attr: Attribute) -> Style {
+ self.attrs.insert(attr);
+ self
+ }
+
+ #[inline]
+ pub fn black(self) -> Style {
+ self.fg(Color::Black)
+ }
+ #[inline]
+ pub fn red(self) -> Style {
+ self.fg(Color::Red)
+ }
+ #[inline]
+ pub fn green(self) -> Style {
+ self.fg(Color::Green)
+ }
+ #[inline]
+ pub fn yellow(self) -> Style {
+ self.fg(Color::Yellow)
+ }
+ #[inline]
+ pub fn blue(self) -> Style {
+ self.fg(Color::Blue)
+ }
+ #[inline]
+ pub fn magenta(self) -> Style {
+ self.fg(Color::Magenta)
+ }
+ #[inline]
+ pub fn cyan(self) -> Style {
+ self.fg(Color::Cyan)
+ }
+ #[inline]
+ pub fn white(self) -> Style {
+ self.fg(Color::White)
+ }
+ #[inline]
+ pub fn color256(self, color: u8) -> Style {
+ self.fg(Color::Color256(color))
+ }
+
+ #[inline]
+ pub fn bright(mut self) -> Style {
+ self.fg_bright = true;
+ self
+ }
+
+ #[inline]
+ pub fn on_black(self) -> Style {
+ self.bg(Color::Black)
+ }
+ #[inline]
+ pub fn on_red(self) -> Style {
+ self.bg(Color::Red)
+ }
+ #[inline]
+ pub fn on_green(self) -> Style {
+ self.bg(Color::Green)
+ }
+ #[inline]
+ pub fn on_yellow(self) -> Style {
+ self.bg(Color::Yellow)
+ }
+ #[inline]
+ pub fn on_blue(self) -> Style {
+ self.bg(Color::Blue)
+ }
+ #[inline]
+ pub fn on_magenta(self) -> Style {
+ self.bg(Color::Magenta)
+ }
+ #[inline]
+ pub fn on_cyan(self) -> Style {
+ self.bg(Color::Cyan)
+ }
+ #[inline]
+ pub fn on_white(self) -> Style {
+ self.bg(Color::White)
+ }
+ #[inline]
+ pub fn on_color256(self, color: u8) -> Style {
+ self.bg(Color::Color256(color))
+ }
+
+ #[inline]
+ pub fn on_bright(mut self) -> Style {
+ self.bg_bright = true;
+ self
+ }
+
+ #[inline]
+ pub fn bold(self) -> Style {
+ self.attr(Attribute::Bold)
+ }
+ #[inline]
+ pub fn dim(self) -> Style {
+ self.attr(Attribute::Dim)
+ }
+ #[inline]
+ pub fn italic(self) -> Style {
+ self.attr(Attribute::Italic)
+ }
+ #[inline]
+ pub fn underlined(self) -> Style {
+ self.attr(Attribute::Underlined)
+ }
+ #[inline]
+ pub fn blink(self) -> Style {
+ self.attr(Attribute::Blink)
+ }
+ #[inline]
+ pub fn blink_fast(self) -> Style {
+ self.attr(Attribute::BlinkFast)
+ }
+ #[inline]
+ pub fn reverse(self) -> Style {
+ self.attr(Attribute::Reverse)
+ }
+ #[inline]
+ pub fn hidden(self) -> Style {
+ self.attr(Attribute::Hidden)
+ }
+ #[inline]
+ pub fn strikethrough(self) -> Style {
+ self.attr(Attribute::StrikeThrough)
+ }
+}
+
+/// Wraps an object for formatting for styling.
+///
+/// Example:
+///
+/// ```rust,no_run
+/// # use console::style;
+/// format!("Hello {}", style("World").cyan());
+/// ```
+///
+/// This is a shortcut for making a new style and applying it
+/// to a value:
+///
+/// ```rust,no_run
+/// # use console::Style;
+/// format!("Hello {}", Style::new().cyan().apply_to("World"));
+/// ```
+pub fn style<D>(val: D) -> StyledObject<D> {
+ Style::new().apply_to(val)
+}
+
+/// A formatting wrapper that can be styled for a terminal.
+#[derive(Clone)]
+pub struct StyledObject<D> {
+ style: Style,
+ val: D,
+}
+
+impl<D> StyledObject<D> {
+ /// Forces styling on or off.
+ ///
+ /// This overrides the automatic detection.
+ #[inline]
+ pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
+ self.style = self.style.force_styling(value);
+ self
+ }
+
+ /// Specifies that style is applying to something being written on stderr
+ #[inline]
+ pub fn for_stderr(mut self) -> StyledObject<D> {
+ self.style = self.style.for_stderr();
+ self
+ }
+
+ /// Specifies that style is applying to something being written on stdout
+ ///
+ /// This is the default
+ #[inline]
+ pub fn for_stdout(mut self) -> StyledObject<D> {
+ self.style = self.style.for_stdout();
+ self
+ }
+
+ /// Sets a foreground color.
+ #[inline]
+ pub fn fg(mut self, color: Color) -> StyledObject<D> {
+ self.style = self.style.fg(color);
+ self
+ }
+
+ /// Sets a background color.
+ #[inline]
+ pub fn bg(mut self, color: Color) -> StyledObject<D> {
+ self.style = self.style.bg(color);
+ self
+ }
+
+ /// Adds a attr.
+ #[inline]
+ pub fn attr(mut self, attr: Attribute) -> StyledObject<D> {
+ self.style = self.style.attr(attr);
+ self
+ }
+
+ #[inline]
+ pub fn black(self) -> StyledObject<D> {
+ self.fg(Color::Black)
+ }
+ #[inline]
+ pub fn red(self) -> StyledObject<D> {
+ self.fg(Color::Red)
+ }
+ #[inline]
+ pub fn green(self) -> StyledObject<D> {
+ self.fg(Color::Green)
+ }
+ #[inline]
+ pub fn yellow(self) -> StyledObject<D> {
+ self.fg(Color::Yellow)
+ }
+ #[inline]
+ pub fn blue(self) -> StyledObject<D> {
+ self.fg(Color::Blue)
+ }
+ #[inline]
+ pub fn magenta(self) -> StyledObject<D> {
+ self.fg(Color::Magenta)
+ }
+ #[inline]
+ pub fn cyan(self) -> StyledObject<D> {
+ self.fg(Color::Cyan)
+ }
+ #[inline]
+ pub fn white(self) -> StyledObject<D> {
+ self.fg(Color::White)
+ }
+ #[inline]
+ pub fn color256(self, color: u8) -> StyledObject<D> {
+ self.fg(Color::Color256(color))
+ }
+
+ #[inline]
+ pub fn bright(mut self) -> StyledObject<D> {
+ self.style = self.style.bright();
+ self
+ }
+
+ #[inline]
+ pub fn on_black(self) -> StyledObject<D> {
+ self.bg(Color::Black)
+ }
+ #[inline]
+ pub fn on_red(self) -> StyledObject<D> {
+ self.bg(Color::Red)
+ }
+ #[inline]
+ pub fn on_green(self) -> StyledObject<D> {
+ self.bg(Color::Green)
+ }
+ #[inline]
+ pub fn on_yellow(self) -> StyledObject<D> {
+ self.bg(Color::Yellow)
+ }
+ #[inline]
+ pub fn on_blue(self) -> StyledObject<D> {
+ self.bg(Color::Blue)
+ }
+ #[inline]
+ pub fn on_magenta(self) -> StyledObject<D> {
+ self.bg(Color::Magenta)
+ }
+ #[inline]
+ pub fn on_cyan(self) -> StyledObject<D> {
+ self.bg(Color::Cyan)
+ }
+ #[inline]
+ pub fn on_white(self) -> StyledObject<D> {
+ self.bg(Color::White)
+ }
+ #[inline]
+ pub fn on_color256(self, color: u8) -> StyledObject<D> {
+ self.bg(Color::Color256(color))
+ }
+
+ #[inline]
+ pub fn on_bright(mut self) -> StyledObject<D> {
+ self.style = self.style.on_bright();
+ self
+ }
+
+ #[inline]
+ pub fn bold(self) -> StyledObject<D> {
+ self.attr(Attribute::Bold)
+ }
+ #[inline]
+ pub fn dim(self) -> StyledObject<D> {
+ self.attr(Attribute::Dim)
+ }
+ #[inline]
+ pub fn italic(self) -> StyledObject<D> {
+ self.attr(Attribute::Italic)
+ }
+ #[inline]
+ pub fn underlined(self) -> StyledObject<D> {
+ self.attr(Attribute::Underlined)
+ }
+ #[inline]
+ pub fn blink(self) -> StyledObject<D> {
+ self.attr(Attribute::Blink)
+ }
+ #[inline]
+ pub fn blink_fast(self) -> StyledObject<D> {
+ self.attr(Attribute::BlinkFast)
+ }
+ #[inline]
+ pub fn reverse(self) -> StyledObject<D> {
+ self.attr(Attribute::Reverse)
+ }
+ #[inline]
+ pub fn hidden(self) -> StyledObject<D> {
+ self.attr(Attribute::Hidden)
+ }
+ #[inline]
+ pub fn strikethrough(self) -> StyledObject<D> {
+ self.attr(Attribute::StrikeThrough)
+ }
+}
+
+macro_rules! impl_fmt {
+ ($name:ident) => {
+ impl<D: fmt::$name> fmt::$name for StyledObject<D> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut reset = false;
+ if self
+ .style
+ .force
+ .unwrap_or_else(|| match self.style.for_stderr {
+ true => colors_enabled_stderr(),
+ false => colors_enabled(),
+ })
+ {
+ if let Some(fg) = self.style.fg {
+ if fg.is_color256() {
+ write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
+ } else if self.style.fg_bright {
+ write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
+ } else {
+ write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
+ }
+ reset = true;
+ }
+ if let Some(bg) = self.style.bg {
+ if bg.is_color256() {
+ write!(f, "\x1b[48;5;{}m", bg.ansi_num())?;
+ } else if self.style.bg_bright {
+ write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
+ } else {
+ write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
+ }
+ reset = true;
+ }
+ for attr in &self.style.attrs {
+ write!(f, "\x1b[{}m", attr.ansi_num())?;
+ reset = true;
+ }
+ }
+ fmt::$name::fmt(&self.val, f)?;
+ if reset {
+ write!(f, "\x1b[0m")?;
+ }
+ Ok(())
+ }
+ }
+ };
+}
+
+impl_fmt!(Binary);
+impl_fmt!(Debug);
+impl_fmt!(Display);
+impl_fmt!(LowerExp);
+impl_fmt!(LowerHex);
+impl_fmt!(Octal);
+impl_fmt!(Pointer);
+impl_fmt!(UpperExp);
+impl_fmt!(UpperHex);
+
+/// "Intelligent" emoji formatter.
+///
+/// This struct intelligently wraps an emoji so that it is rendered
+/// only on systems that want emojis and renders a fallback on others.
+///
+/// Example:
+///
+/// ```rust
+/// use console::Emoji;
+/// println!("[3/4] {}Downloading ...", Emoji("🚚 ", ""));
+/// println!("[4/4] {} Done!", Emoji("✨", ":-)"));
+/// ```
+#[derive(Copy, Clone)]
+pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
+
+impl<'a, 'b> Emoji<'a, 'b> {
+ pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
+ Emoji(emoji, fallback)
+ }
+}
+
+impl<'a, 'b> fmt::Display for Emoji<'a, 'b> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if wants_emoji() {
+ write!(f, "{}", self.0)
+ } else {
+ write!(f, "{}", self.1)
+ }
+ }
+}
+
+fn str_width(s: &str) -> usize {
+ #[cfg(feature = "unicode-width")]
+ {
+ use unicode_width::UnicodeWidthStr;
+ s.width()
+ }
+ #[cfg(not(feature = "unicode-width"))]
+ {
+ s.chars().count()
+ }
+}
+
+#[cfg(feature = "ansi-parsing")]
+fn char_width(c: char) -> usize {
+ #[cfg(feature = "unicode-width")]
+ {
+ use unicode_width::UnicodeWidthChar;
+ c.width().unwrap_or(0)
+ }
+ #[cfg(not(feature = "unicode-width"))]
+ {
+ let _c = c;
+ 1
+ }
+}
+
+/// Truncates a string to a certain number of characters.
+///
+/// This ensures that escape codes are not screwed up in the process.
+/// If the maximum length is hit the string will be truncated but
+/// escapes code will still be honored. If truncation takes place
+/// the tail string will be appended.
+pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
+ #[cfg(feature = "ansi-parsing")]
+ {
+ use std::cmp::Ordering;
+ let mut iter = AnsiCodeIterator::new(s);
+ let mut length = 0;
+ let mut rv = None;
+
+ while let Some(item) = iter.next() {
+ match item {
+ (s, false) => {
+ if rv.is_none() {
+ if str_width(s) + length > width - str_width(tail) {
+ let ts = iter.current_slice();
+
+ let mut s_byte = 0;
+ let mut s_width = 0;
+ let rest_width = width - str_width(tail) - length;
+ for c in s.chars() {
+ s_byte += c.len_utf8();
+ s_width += char_width(c);
+ match s_width.cmp(&rest_width) {
+ Ordering::Equal => break,
+ Ordering::Greater => {
+ s_byte -= c.len_utf8();
+ break;
+ }
+ Ordering::Less => continue,
+ }
+ }
+
+ let idx = ts.len() - s.len() + s_byte;
+ let mut buf = ts[..idx].to_string();
+ buf.push_str(tail);
+ rv = Some(buf);
+ }
+ length += str_width(s);
+ }
+ }
+ (s, true) => {
+ if rv.is_some() {
+ rv.as_mut().unwrap().push_str(s);
+ }
+ }
+ }
+ }
+
+ if let Some(buf) = rv {
+ Cow::Owned(buf)
+ } else {
+ Cow::Borrowed(s)
+ }
+ }
+
+ #[cfg(not(feature = "ansi-parsing"))]
+ {
+ if s.len() <= width - tail.len() {
+ Cow::Borrowed(s)
+ } else {
+ Cow::Owned(format!(
+ "{}{}",
+ s.get(..width - tail.len()).unwrap_or_default(),
+ tail
+ ))
+ }
+ }
+}
+
+/// Pads a string to fill a certain number of characters.
+///
+/// This will honor ansi codes correctly and allows you to align a string
+/// on the left, right or centered. Additionally truncation can be enabled
+/// by setting `truncate` to a string that should be used as a truncation
+/// marker.
+pub fn pad_str<'a>(
+ s: &'a str,
+ width: usize,
+ align: Alignment,
+ truncate: Option<&str>,
+) -> Cow<'a, str> {
+ pad_str_with(s, width, align, truncate, ' ')
+}
+/// Pads a string with specific padding to fill a certain number of characters.
+///
+/// This will honor ansi codes correctly and allows you to align a string
+/// on the left, right or centered. Additionally truncation can be enabled
+/// by setting `truncate` to a string that should be used as a truncation
+/// marker.
+pub fn pad_str_with<'a>(
+ s: &'a str,
+ width: usize,
+ align: Alignment,
+ truncate: Option<&str>,
+ pad: char,
+) -> Cow<'a, str> {
+ let cols = measure_text_width(s);
+
+ if cols >= width {
+ return match truncate {
+ None => Cow::Borrowed(s),
+ Some(tail) => truncate_str(s, width, tail),
+ };
+ }
+
+ let diff = width - cols;
+
+ let (left_pad, right_pad) = match align {
+ Alignment::Left => (0, diff),
+ Alignment::Right => (diff, 0),
+ Alignment::Center => (diff / 2, diff - diff / 2),
+ };
+
+ let mut rv = String::new();
+ for _ in 0..left_pad {
+ rv.push(pad);
+ }
+ rv.push_str(s);
+ for _ in 0..right_pad {
+ rv.push(pad);
+ }
+ Cow::Owned(rv)
+}
+
+#[test]
+fn test_text_width() {
+ let s = style("foo")
+ .red()
+ .on_black()
+ .bold()
+ .force_styling(true)
+ .to_string();
+ assert_eq!(
+ measure_text_width(&s),
+ if cfg!(feature = "ansi-parsing") {
+ 3
+ } else if cfg!(feature = "unicode-width") {
+ 17
+ } else {
+ 21
+ }
+ );
+}
+
+#[test]
+#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
+fn test_truncate_str() {
+ let s = format!("foo {}", style("bar").red().force_styling(true));
+ assert_eq!(
+ &truncate_str(&s, 5, ""),
+ &format!("foo {}", style("b").red().force_styling(true))
+ );
+ let s = format!("foo {}", style("bar").red().force_styling(true));
+ assert_eq!(
+ &truncate_str(&s, 5, "!"),
+ &format!("foo {}", style("!").red().force_styling(true))
+ );
+ let s = format!("foo {} baz", style("bar").red().force_styling(true));
+ assert_eq!(
+ &truncate_str(&s, 10, "..."),
+ &format!("foo {}...", style("bar").red().force_styling(true))
+ );
+ let s = format!("foo {}", style("バー").red().force_styling(true));
+ assert_eq!(
+ &truncate_str(&s, 5, ""),
+ &format!("foo {}", style("").red().force_styling(true))
+ );
+ let s = format!("foo {}", style("バー").red().force_styling(true));
+ assert_eq!(
+ &truncate_str(&s, 6, ""),
+ &format!("foo {}", style("バ").red().force_styling(true))
+ );
+}
+
+#[test]
+fn test_truncate_str_no_ansi() {
+ assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
+ assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
+ assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
+}
+
+#[test]
+fn test_pad_str() {
+ assert_eq!(pad_str("foo", 7, Alignment::Center, None), " foo ");
+ assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo ");
+ assert_eq!(pad_str("foo", 7, Alignment::Right, None), " foo");
+ assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
+ assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
+ assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
+ assert_eq!(
+ pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
+ "foo..."
+ );
+}
+
+#[test]
+fn test_pad_str_with() {
+ assert_eq!(
+ pad_str_with("foo", 7, Alignment::Center, None, '#'),
+ "##foo##"
+ );
+ assert_eq!(
+ pad_str_with("foo", 7, Alignment::Left, None, '#'),
+ "foo####"
+ );
+ assert_eq!(
+ pad_str_with("foo", 7, Alignment::Right, None, '#'),
+ "####foo"
+ );
+ assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
+ assert_eq!(
+ pad_str_with("foobar", 3, Alignment::Left, None, '#'),
+ "foobar"
+ );
+ assert_eq!(
+ pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
+ "foo"
+ );
+ assert_eq!(
+ pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
+ "foo..."
+ );
+}
diff --git a/vendor/console/src/wasm_term.rs b/vendor/console/src/wasm_term.rs
new file mode 100644
index 0000000..764bc34
--- /dev/null
+++ b/vendor/console/src/wasm_term.rs
@@ -0,0 +1,54 @@
+use std::fmt::Display;
+use std::io;
+
+use crate::kb::Key;
+use crate::term::Term;
+
+pub use crate::common_term::*;
+
+pub const DEFAULT_WIDTH: u16 = 80;
+
+#[inline]
+pub fn is_a_terminal(_out: &Term) -> bool {
+ #[cfg(target = "wasm32-wasi")]
+ {
+ unsafe { libc::isatty(out.as_raw_fd()) != 0 }
+ }
+ #[cfg(not(target = "wasm32-wasi"))]
+ {
+ false
+ }
+}
+
+#[inline]
+pub fn is_a_color_terminal(_out: &Term) -> bool {
+ // We currently never report color terminals. For discussion see
+ // the issue in the WASI repo: https://github.com/WebAssembly/WASI/issues/162
+ false
+}
+
+#[inline]
+pub fn terminal_size(_out: &Term) -> Option<(u16, u16)> {
+ None
+}
+
+pub fn read_secure() -> io::Result<String> {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "unsupported operation",
+ ))
+}
+
+pub fn read_single_key() -> io::Result<Key> {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "unsupported operation",
+ ))
+}
+
+#[inline]
+pub fn wants_emoji() -> bool {
+ false
+}
+
+pub fn set_title<T: Display>(_title: T) {}
diff --git a/vendor/console/src/windows_term/colors.rs b/vendor/console/src/windows_term/colors.rs
new file mode 100644
index 0000000..dc8209d
--- /dev/null
+++ b/vendor/console/src/windows_term/colors.rs
@@ -0,0 +1,451 @@
+use std::io;
+use std::mem;
+use std::os::windows::io::AsRawHandle;
+use std::str::Bytes;
+
+use windows_sys::Win32::Foundation::HANDLE;
+use windows_sys::Win32::System::Console::{
+ GetConsoleScreenBufferInfo, SetConsoleTextAttribute, CONSOLE_SCREEN_BUFFER_INFO,
+ FOREGROUND_BLUE as FG_BLUE, FOREGROUND_GREEN as FG_GREEN, FOREGROUND_INTENSITY as FG_INTENSITY,
+ FOREGROUND_RED as FG_RED,
+};
+
+use crate::Term;
+
+type WORD = u16;
+
+const FG_CYAN: WORD = FG_BLUE | FG_GREEN;
+const FG_MAGENTA: WORD = FG_BLUE | FG_RED;
+const FG_YELLOW: WORD = FG_GREEN | FG_RED;
+const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED;
+
+/// Query the given handle for information about the console's screen buffer.
+///
+/// The given handle should represent a console. Otherwise, an error is
+/// returned.
+///
+/// This corresponds to calling [`GetConsoleScreenBufferInfo`].
+///
+/// [`GetConsoleScreenBufferInfo`]: https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
+pub fn screen_buffer_info(h: HANDLE) -> io::Result<ScreenBufferInfo> {
+ unsafe {
+ let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
+ let rc = GetConsoleScreenBufferInfo(h, &mut info);
+ if rc == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(ScreenBufferInfo(info))
+ }
+}
+
+/// Set the text attributes of the console represented by the given handle.
+///
+/// This corresponds to calling [`SetConsoleTextAttribute`].
+///
+/// [`SetConsoleTextAttribute`]: https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute
+pub fn set_text_attributes(h: HANDLE, attributes: u16) -> io::Result<()> {
+ if unsafe { SetConsoleTextAttribute(h, attributes) } == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+}
+
+/// Represents console screen buffer information such as size, cursor position
+/// and styling attributes.
+///
+/// This wraps a [`CONSOLE_SCREEN_BUFFER_INFO`].
+///
+/// [`CONSOLE_SCREEN_BUFFER_INFO`]: https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
+#[derive(Clone)]
+pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
+
+impl ScreenBufferInfo {
+ /// Returns the character attributes associated with this console.
+ ///
+ /// This corresponds to `wAttributes`.
+ ///
+ /// See [`char info`] for more details.
+ ///
+ /// [`char info`]: https://docs.microsoft.com/en-us/windows/console/char-info-str
+ pub fn attributes(&self) -> u16 {
+ self.0.wAttributes
+ }
+}
+
+/// A Windows console.
+///
+/// This represents a very limited set of functionality available to a Windows
+/// console. In particular, it can only change text attributes such as color
+/// and intensity. This may grow over time. If you need more routines, please
+/// file an issue and/or PR.
+///
+/// There is no way to "write" to this console. Simply write to
+/// stdout or stderr instead, while interleaving instructions to the console
+/// to change text attributes.
+///
+/// A common pitfall when using a console is to forget to flush writes to
+/// stdout before setting new text attributes.
+#[derive(Debug)]
+pub struct Console {
+ kind: HandleKind,
+ start_attr: TextAttributes,
+ cur_attr: TextAttributes,
+}
+
+#[derive(Clone, Copy, Debug)]
+enum HandleKind {
+ Stdout,
+ Stderr,
+}
+
+impl HandleKind {
+ fn handle(&self) -> HANDLE {
+ match *self {
+ HandleKind::Stdout => io::stdout().as_raw_handle() as HANDLE,
+ HandleKind::Stderr => io::stderr().as_raw_handle() as HANDLE,
+ }
+ }
+}
+
+impl Console {
+ /// Get a console for a standard I/O stream.
+ fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
+ let h = kind.handle();
+ let info = screen_buffer_info(h)?;
+ let attr = TextAttributes::from_word(info.attributes());
+ Ok(Console {
+ kind: kind,
+ start_attr: attr,
+ cur_attr: attr,
+ })
+ }
+
+ /// Create a new Console to stdout.
+ ///
+ /// If there was a problem creating the console, then an error is returned.
+ pub fn stdout() -> io::Result<Console> {
+ Self::create_for_stream(HandleKind::Stdout)
+ }
+
+ /// Create a new Console to stderr.
+ ///
+ /// If there was a problem creating the console, then an error is returned.
+ pub fn stderr() -> io::Result<Console> {
+ Self::create_for_stream(HandleKind::Stderr)
+ }
+
+ /// Applies the current text attributes.
+ fn set(&mut self) -> io::Result<()> {
+ set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
+ }
+
+ /// Apply the given intensity and color attributes to the console
+ /// foreground.
+ ///
+ /// If there was a problem setting attributes on the console, then an error
+ /// is returned.
+ pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
+ self.cur_attr.fg_color = color;
+ self.cur_attr.fg_intense = intense;
+ self.set()
+ }
+
+ /// Apply the given intensity and color attributes to the console
+ /// background.
+ ///
+ /// If there was a problem setting attributes on the console, then an error
+ /// is returned.
+ pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
+ self.cur_attr.bg_color = color;
+ self.cur_attr.bg_intense = intense;
+ self.set()
+ }
+
+ /// Reset the console text attributes to their original settings.
+ ///
+ /// The original settings correspond to the text attributes on the console
+ /// when this `Console` value was created.
+ ///
+ /// If there was a problem setting attributes on the console, then an error
+ /// is returned.
+ pub fn reset(&mut self) -> io::Result<()> {
+ self.cur_attr = self.start_attr;
+ self.set()
+ }
+}
+
+/// A representation of text attributes for the Windows console.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+struct TextAttributes {
+ fg_color: Color,
+ fg_intense: Intense,
+ bg_color: Color,
+ bg_intense: Intense,
+}
+
+impl TextAttributes {
+ fn to_word(&self) -> WORD {
+ let mut w = 0;
+ w |= self.fg_color.to_fg();
+ w |= self.fg_intense.to_fg();
+ w |= self.bg_color.to_bg();
+ w |= self.bg_intense.to_bg();
+ w
+ }
+
+ fn from_word(word: WORD) -> TextAttributes {
+ TextAttributes {
+ fg_color: Color::from_fg(word),
+ fg_intense: Intense::from_fg(word),
+ bg_color: Color::from_bg(word),
+ bg_intense: Intense::from_bg(word),
+ }
+ }
+}
+
+/// Whether to use intense colors or not.
+#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Intense {
+ Yes,
+ No,
+}
+
+impl Intense {
+ fn to_bg(&self) -> WORD {
+ self.to_fg() << 4
+ }
+
+ fn from_bg(word: WORD) -> Intense {
+ Intense::from_fg(word >> 4)
+ }
+
+ fn to_fg(&self) -> WORD {
+ match *self {
+ Intense::No => 0,
+ Intense::Yes => FG_INTENSITY,
+ }
+ }
+
+ fn from_fg(word: WORD) -> Intense {
+ if word & FG_INTENSITY > 0 {
+ Intense::Yes
+ } else {
+ Intense::No
+ }
+ }
+}
+
+/// The set of available colors for use with a Windows console.
+#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Color {
+ Black,
+ Blue,
+ Green,
+ Red,
+ Cyan,
+ Magenta,
+ Yellow,
+ White,
+}
+
+impl Color {
+ fn to_bg(&self) -> WORD {
+ self.to_fg() << 4
+ }
+
+ fn from_bg(word: WORD) -> Color {
+ Color::from_fg(word >> 4)
+ }
+
+ fn to_fg(&self) -> WORD {
+ match *self {
+ Color::Black => 0,
+ Color::Blue => FG_BLUE,
+ Color::Green => FG_GREEN,
+ Color::Red => FG_RED,
+ Color::Cyan => FG_CYAN,
+ Color::Magenta => FG_MAGENTA,
+ Color::Yellow => FG_YELLOW,
+ Color::White => FG_WHITE,
+ }
+ }
+
+ fn from_fg(word: WORD) -> Color {
+ match word & 0b111 {
+ FG_BLUE => Color::Blue,
+ FG_GREEN => Color::Green,
+ FG_RED => Color::Red,
+ FG_CYAN => Color::Cyan,
+ FG_MAGENTA => Color::Magenta,
+ FG_YELLOW => Color::Yellow,
+ FG_WHITE => Color::White,
+ _ => Color::Black,
+ }
+ }
+}
+
+pub fn console_colors(out: &Term, mut con: Console, bytes: &[u8]) -> io::Result<()> {
+ use crate::ansi::AnsiCodeIterator;
+ use std::str::from_utf8;
+
+ let s = from_utf8(bytes).expect("data to be printed is not an ansi string");
+ let mut iter = AnsiCodeIterator::new(s);
+
+ while !iter.rest_slice().is_empty() {
+ if let Some((part, is_esc)) = iter.next() {
+ if !is_esc {
+ out.write_through_common(part.as_bytes())?;
+ } else if part == "\x1b[0m" {
+ con.reset()?;
+ } else if let Some((intense, color, fg_bg)) = driver(parse_color, part) {
+ match fg_bg {
+ FgBg::Foreground => con.fg(intense, color),
+ FgBg::Background => con.bg(intense, color),
+ }?;
+ } else if driver(parse_attr, part).is_none() {
+ out.write_through_common(part.as_bytes())?;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum FgBg {
+ Foreground,
+ Background,
+}
+
+impl FgBg {
+ fn new(byte: u8) -> Option<Self> {
+ match byte {
+ b'3' => Some(Self::Foreground),
+ b'4' => Some(Self::Background),
+ _ => None,
+ }
+ }
+}
+
+fn driver<Out>(parse: fn(Bytes<'_>) -> Option<Out>, part: &str) -> Option<Out> {
+ let mut bytes = part.bytes();
+
+ loop {
+ while bytes.next()? != b'\x1b' {}
+
+ if let ret @ Some(_) = (parse)(bytes.clone()) {
+ return ret;
+ }
+ }
+}
+
+// `driver(parse_color, s)` parses the equivalent of the regex
+// \x1b\[(3|4)8;5;(8|9|1[0-5])m
+// for intense or
+// \x1b\[(3|4)([0-7])m
+// for normal
+fn parse_color(mut bytes: Bytes<'_>) -> Option<(Intense, Color, FgBg)> {
+ parse_prefix(&mut bytes)?;
+
+ let fg_bg = FgBg::new(bytes.next()?)?;
+ let (intense, color) = match bytes.next()? {
+ b @ b'0'..=b'7' => (Intense::No, normal_color_ansi_from_byte(b)?),
+ b'8' => {
+ if &[bytes.next()?, bytes.next()?, bytes.next()?] != b";5;" {
+ return None;
+ }
+ (Intense::Yes, parse_intense_color_ansi(&mut bytes)?)
+ }
+ _ => return None,
+ };
+
+ parse_suffix(&mut bytes)?;
+ Some((intense, color, fg_bg))
+}
+
+// `driver(parse_attr, s)` parses the equivalent of the regex
+// \x1b\[([1-8])m
+fn parse_attr(mut bytes: Bytes<'_>) -> Option<u8> {
+ parse_prefix(&mut bytes)?;
+ let attr = match bytes.next()? {
+ attr @ b'1'..=b'8' => attr,
+ _ => return None,
+ };
+ parse_suffix(&mut bytes)?;
+ Some(attr)
+}
+
+fn parse_prefix(bytes: &mut Bytes<'_>) -> Option<()> {
+ if bytes.next()? == b'[' {
+ Some(())
+ } else {
+ None
+ }
+}
+
+fn parse_intense_color_ansi(bytes: &mut Bytes<'_>) -> Option<Color> {
+ let color = match bytes.next()? {
+ b'8' => Color::Black,
+ b'9' => Color::Red,
+ b'1' => match bytes.next()? {
+ b'0' => Color::Green,
+ b'1' => Color::Yellow,
+ b'2' => Color::Blue,
+ b'3' => Color::Magenta,
+ b'4' => Color::Cyan,
+ b'5' => Color::White,
+ _ => return None,
+ },
+ _ => return None,
+ };
+ Some(color)
+}
+
+fn normal_color_ansi_from_byte(b: u8) -> Option<Color> {
+ let color = match b {
+ b'0' => Color::Black,
+ b'1' => Color::Red,
+ b'2' => Color::Green,
+ b'3' => Color::Yellow,
+ b'4' => Color::Blue,
+ b'5' => Color::Magenta,
+ b'6' => Color::Cyan,
+ b'7' => Color::White,
+ _ => return None,
+ };
+ Some(color)
+}
+
+fn parse_suffix(bytes: &mut Bytes<'_>) -> Option<()> {
+ if bytes.next()? == b'm' {
+ Some(())
+ } else {
+ None
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn color_parsing() {
+ let intense_color = "leading bytes \x1b[38;5;10m trailing bytes";
+ let parsed = driver(parse_color, intense_color).unwrap();
+ assert_eq!(parsed, (Intense::Yes, Color::Green, FgBg::Foreground));
+
+ let normal_color = "leading bytes \x1b[40m trailing bytes";
+ let parsed = driver(parse_color, normal_color).unwrap();
+ assert_eq!(parsed, (Intense::No, Color::Black, FgBg::Background));
+ }
+
+ #[test]
+ fn attr_parsing() {
+ let attr = "leading bytes \x1b[1m trailing bytes";
+ let parsed = driver(parse_attr, attr).unwrap();
+ assert_eq!(parsed, b'1');
+ }
+}
diff --git a/vendor/console/src/windows_term/mod.rs b/vendor/console/src/windows_term/mod.rs
new file mode 100644
index 0000000..c4ec193
--- /dev/null
+++ b/vendor/console/src/windows_term/mod.rs
@@ -0,0 +1,563 @@
+use std::cmp;
+use std::env;
+use std::ffi::OsStr;
+use std::fmt::Display;
+use std::io;
+use std::iter::once;
+use std::mem;
+use std::os::raw::c_void;
+use std::os::windows::ffi::OsStrExt;
+use std::os::windows::io::AsRawHandle;
+use std::slice;
+use std::{char, mem::MaybeUninit};
+
+use encode_unicode::error::InvalidUtf16Tuple;
+use encode_unicode::CharExt;
+use windows_sys::Win32::Foundation::{CHAR, HANDLE, INVALID_HANDLE_VALUE, MAX_PATH};
+use windows_sys::Win32::Storage::FileSystem::{
+ FileNameInfo, GetFileInformationByHandleEx, FILE_NAME_INFO,
+};
+use windows_sys::Win32::System::Console::{
+ FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetConsoleCursorInfo, GetConsoleMode,
+ GetConsoleScreenBufferInfo, GetNumberOfConsoleInputEvents, GetStdHandle, ReadConsoleInputW,
+ SetConsoleCursorInfo, SetConsoleCursorPosition, SetConsoleMode, SetConsoleTitleW,
+ CONSOLE_CURSOR_INFO, CONSOLE_SCREEN_BUFFER_INFO, COORD, INPUT_RECORD, KEY_EVENT,
+ KEY_EVENT_RECORD, STD_ERROR_HANDLE, STD_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+};
+use windows_sys::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY;
+
+use crate::common_term;
+use crate::kb::Key;
+use crate::term::{Term, TermTarget};
+
+#[cfg(feature = "windows-console-colors")]
+mod colors;
+
+#[cfg(feature = "windows-console-colors")]
+pub use self::colors::*;
+
+const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x4;
+pub const DEFAULT_WIDTH: u16 = 79;
+
+pub fn as_handle(term: &Term) -> HANDLE {
+ // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE
+ term.as_raw_handle() as HANDLE
+}
+
+pub fn is_a_terminal(out: &Term) -> bool {
+ let (fd, others) = match out.target() {
+ TermTarget::Stdout => (STD_OUTPUT_HANDLE, [STD_INPUT_HANDLE, STD_ERROR_HANDLE]),
+ TermTarget::Stderr => (STD_ERROR_HANDLE, [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE]),
+ };
+
+ if unsafe { console_on_any(&[fd]) } {
+ // False positives aren't possible. If we got a console then
+ // we definitely have a tty on stdin.
+ return true;
+ }
+
+ // At this point, we *could* have a false negative. We can determine that
+ // this is true negative if we can detect the presence of a console on
+ // any of the other streams. If another stream has a console, then we know
+ // we're in a Windows console and can therefore trust the negative.
+ if unsafe { console_on_any(&others) } {
+ return false;
+ }
+
+ msys_tty_on(out)
+}
+
+pub fn is_a_color_terminal(out: &Term) -> bool {
+ if !is_a_terminal(out) {
+ return false;
+ }
+ if msys_tty_on(out) {
+ return match env::var("TERM") {
+ Ok(term) => term != "dumb",
+ Err(_) => true,
+ };
+ }
+ enable_ansi_on(out)
+}
+
+fn enable_ansi_on(out: &Term) -> bool {
+ unsafe {
+ let handle = as_handle(out);
+
+ let mut dw_mode = 0;
+ if GetConsoleMode(handle, &mut dw_mode) == 0 {
+ return false;
+ }
+
+ dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ if SetConsoleMode(handle, dw_mode) == 0 {
+ return false;
+ }
+
+ true
+ }
+}
+
+unsafe fn console_on_any(fds: &[STD_HANDLE]) -> bool {
+ for &fd in fds {
+ let mut out = 0;
+ let handle = GetStdHandle(fd);
+ if GetConsoleMode(handle, &mut out) != 0 {
+ return true;
+ }
+ }
+ false
+}
+
+pub fn terminal_size(out: &Term) -> Option<(u16, u16)> {
+ use windows_sys::Win32::System::Console::SMALL_RECT;
+
+ // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE
+ let handle = out.as_raw_handle();
+ let hand = handle as windows_sys::Win32::Foundation::HANDLE;
+
+ if hand == INVALID_HANDLE_VALUE {
+ return None;
+ }
+
+ let zc = COORD { X: 0, Y: 0 };
+ let mut csbi = CONSOLE_SCREEN_BUFFER_INFO {
+ dwSize: zc,
+ dwCursorPosition: zc,
+ wAttributes: 0,
+ srWindow: SMALL_RECT {
+ Left: 0,
+ Top: 0,
+ Right: 0,
+ Bottom: 0,
+ },
+ dwMaximumWindowSize: zc,
+ };
+ if unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } == 0 {
+ return None;
+ }
+
+ let rows = (csbi.srWindow.Bottom - csbi.srWindow.Top + 1) as u16;
+ let columns = (csbi.srWindow.Right - csbi.srWindow.Left + 1) as u16;
+
+ Some((rows, columns))
+}
+
+pub fn move_cursor_to(out: &Term, x: usize, y: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::move_cursor_to(out, x, y);
+ }
+ if let Some((hand, _)) = get_console_screen_buffer_info(as_handle(out)) {
+ unsafe {
+ SetConsoleCursorPosition(
+ hand,
+ COORD {
+ X: x as i16,
+ Y: y as i16,
+ },
+ );
+ }
+ }
+ Ok(())
+}
+
+pub fn move_cursor_up(out: &Term, n: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::move_cursor_up(out, n);
+ }
+
+ if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize - n)?;
+ }
+ Ok(())
+}
+
+pub fn move_cursor_down(out: &Term, n: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::move_cursor_down(out, n);
+ }
+
+ if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize + n)?;
+ }
+ Ok(())
+}
+
+pub fn move_cursor_left(out: &Term, n: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::move_cursor_left(out, n);
+ }
+
+ if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ move_cursor_to(
+ out,
+ csbi.dwCursorPosition.X as usize - n,
+ csbi.dwCursorPosition.Y as usize,
+ )?;
+ }
+ Ok(())
+}
+
+pub fn move_cursor_right(out: &Term, n: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::move_cursor_right(out, n);
+ }
+
+ if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ move_cursor_to(
+ out,
+ csbi.dwCursorPosition.X as usize + n,
+ csbi.dwCursorPosition.Y as usize,
+ )?;
+ }
+ Ok(())
+}
+
+pub fn clear_line(out: &Term) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::clear_line(out);
+ }
+ if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ unsafe {
+ let width = csbi.srWindow.Right - csbi.srWindow.Left;
+ let pos = COORD {
+ X: 0,
+ Y: csbi.dwCursorPosition.Y,
+ };
+ let mut written = 0;
+ FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written);
+ FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written);
+ SetConsoleCursorPosition(hand, pos);
+ }
+ }
+ Ok(())
+}
+
+pub fn clear_chars(out: &Term, n: usize) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::clear_chars(out, n);
+ }
+ if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ unsafe {
+ let width = cmp::min(csbi.dwCursorPosition.X, n as i16);
+ let pos = COORD {
+ X: csbi.dwCursorPosition.X - width,
+ Y: csbi.dwCursorPosition.Y,
+ };
+ let mut written = 0;
+ FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written);
+ FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written);
+ SetConsoleCursorPosition(hand, pos);
+ }
+ }
+ Ok(())
+}
+
+pub fn clear_screen(out: &Term) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::clear_screen(out);
+ }
+ if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ unsafe {
+ let cells = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32; // as u32, or else this causes stack overflows.
+ let pos = COORD { X: 0, Y: 0 };
+ let mut written = 0;
+ FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed.
+ FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written);
+ SetConsoleCursorPosition(hand, pos);
+ }
+ }
+ Ok(())
+}
+
+pub fn clear_to_end_of_screen(out: &Term) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::clear_to_end_of_screen(out);
+ }
+ if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
+ unsafe {
+ let bottom = csbi.srWindow.Right as u32 * csbi.srWindow.Bottom as u32;
+ let cells = bottom - (csbi.dwCursorPosition.X as u32 * csbi.dwCursorPosition.Y as u32); // as u32, or else this causes stack overflows.
+ let pos = COORD {
+ X: 0,
+ Y: csbi.dwCursorPosition.Y,
+ };
+ let mut written = 0;
+ FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed.
+ FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written);
+ SetConsoleCursorPosition(hand, pos);
+ }
+ }
+ Ok(())
+}
+
+pub fn show_cursor(out: &Term) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::show_cursor(out);
+ }
+ if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) {
+ unsafe {
+ cci.bVisible = 1;
+ SetConsoleCursorInfo(hand, &cci);
+ }
+ }
+ Ok(())
+}
+
+pub fn hide_cursor(out: &Term) -> io::Result<()> {
+ if out.is_msys_tty {
+ return common_term::hide_cursor(out);
+ }
+ if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) {
+ unsafe {
+ cci.bVisible = 0;
+ SetConsoleCursorInfo(hand, &cci);
+ }
+ }
+ Ok(())
+}
+
+fn get_console_screen_buffer_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_SCREEN_BUFFER_INFO)> {
+ let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = unsafe { mem::zeroed() };
+ match unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } {
+ 0 => None,
+ _ => Some((hand, csbi)),
+ }
+}
+
+fn get_console_cursor_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_CURSOR_INFO)> {
+ let mut cci: CONSOLE_CURSOR_INFO = unsafe { mem::zeroed() };
+ match unsafe { GetConsoleCursorInfo(hand, &mut cci) } {
+ 0 => None,
+ _ => Some((hand, cci)),
+ }
+}
+
+pub fn key_from_key_code(code: VIRTUAL_KEY) -> Key {
+ use windows_sys::Win32::UI::Input::KeyboardAndMouse;
+
+ match code {
+ KeyboardAndMouse::VK_LEFT => Key::ArrowLeft,
+ KeyboardAndMouse::VK_RIGHT => Key::ArrowRight,
+ KeyboardAndMouse::VK_UP => Key::ArrowUp,
+ KeyboardAndMouse::VK_DOWN => Key::ArrowDown,
+ KeyboardAndMouse::VK_RETURN => Key::Enter,
+ KeyboardAndMouse::VK_ESCAPE => Key::Escape,
+ KeyboardAndMouse::VK_BACK => Key::Backspace,
+ KeyboardAndMouse::VK_TAB => Key::Tab,
+ KeyboardAndMouse::VK_HOME => Key::Home,
+ KeyboardAndMouse::VK_END => Key::End,
+ KeyboardAndMouse::VK_DELETE => Key::Del,
+ KeyboardAndMouse::VK_SHIFT => Key::Shift,
+ KeyboardAndMouse::VK_MENU => Key::Alt,
+ _ => Key::Unknown,
+ }
+}
+
+pub fn read_secure() -> io::Result<String> {
+ let mut rv = String::new();
+ loop {
+ match read_single_key()? {
+ Key::Enter => {
+ break;
+ }
+ Key::Char('\x08') => {
+ if !rv.is_empty() {
+ let new_len = rv.len() - 1;
+ rv.truncate(new_len);
+ }
+ }
+ Key::Char(c) => {
+ rv.push(c);
+ }
+ _ => {}
+ }
+ }
+ Ok(rv)
+}
+
+pub fn read_single_key() -> io::Result<Key> {
+ let key_event = read_key_event()?;
+
+ let unicode_char = unsafe { key_event.uChar.UnicodeChar };
+ if unicode_char == 0 {
+ Ok(key_from_key_code(key_event.wVirtualKeyCode))
+ } else {
+ // This is a unicode character, in utf-16. Try to decode it by itself.
+ match char::from_utf16_tuple((unicode_char, None)) {
+ Ok(c) => {
+ // Maintain backward compatibility. The previous implementation (_getwch()) would return
+ // a special keycode for `Enter`, while ReadConsoleInputW() prefers to use '\r'.
+ if c == '\r' {
+ Ok(Key::Enter)
+ } else if c == '\x08' {
+ Ok(Key::Backspace)
+ } else if c == '\x1B' {
+ Ok(Key::Escape)
+ } else {
+ Ok(Key::Char(c))
+ }
+ }
+ // This is part of a surrogate pair. Try to read the second half.
+ Err(InvalidUtf16Tuple::MissingSecond) => {
+ // Confirm that there is a next character to read.
+ if get_key_event_count()? == 0 {
+ let message = format!(
+ "Read invlid utf16 {}: {}",
+ unicode_char,
+ InvalidUtf16Tuple::MissingSecond
+ );
+ return Err(io::Error::new(io::ErrorKind::InvalidData, message));
+ }
+
+ // Read the next character.
+ let next_event = read_key_event()?;
+ let next_surrogate = unsafe { next_event.uChar.UnicodeChar };
+
+ // Attempt to decode it.
+ match char::from_utf16_tuple((unicode_char, Some(next_surrogate))) {
+ Ok(c) => Ok(Key::Char(c)),
+
+ // Return an InvalidData error. This is the recommended value for UTF-related I/O errors.
+ // (This error is given when reading a non-UTF8 file into a String, for example.)
+ Err(e) => {
+ let message = format!(
+ "Read invalid surrogate pair ({}, {}): {}",
+ unicode_char, next_surrogate, e
+ );
+ Err(io::Error::new(io::ErrorKind::InvalidData, message))
+ }
+ }
+ }
+
+ // Return an InvalidData error. This is the recommended value for UTF-related I/O errors.
+ // (This error is given when reading a non-UTF8 file into a String, for example.)
+ Err(e) => {
+ let message = format!("Read invalid utf16 {}: {}", unicode_char, e);
+ Err(io::Error::new(io::ErrorKind::InvalidData, message))
+ }
+ }
+ }
+}
+
+fn get_stdin_handle() -> io::Result<HANDLE> {
+ let handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
+ if handle == INVALID_HANDLE_VALUE {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(handle)
+ }
+}
+
+/// Get the number of pending events in the ReadConsoleInput queue. Note that while
+/// these aren't necessarily key events, the only way that multiple events can be
+/// put into the queue simultaneously is if a unicode character spanning multiple u16's
+/// is read.
+///
+/// Therefore, this is accurate as long as at least one KEY_EVENT has already been read.
+fn get_key_event_count() -> io::Result<u32> {
+ let handle = get_stdin_handle()?;
+ let mut event_count: u32 = unsafe { mem::zeroed() };
+
+ let success = unsafe { GetNumberOfConsoleInputEvents(handle, &mut event_count) };
+ if success == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(event_count)
+ }
+}
+
+fn read_key_event() -> io::Result<KEY_EVENT_RECORD> {
+ let handle = get_stdin_handle()?;
+ let mut buffer: INPUT_RECORD = unsafe { mem::zeroed() };
+
+ let mut events_read: u32 = unsafe { mem::zeroed() };
+
+ let mut key_event: KEY_EVENT_RECORD;
+ loop {
+ let success = unsafe { ReadConsoleInputW(handle, &mut buffer, 1, &mut events_read) };
+ if success == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ if events_read == 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "ReadConsoleInput returned no events, instead of waiting for an event",
+ ));
+ }
+
+ if events_read == 1 && buffer.EventType != KEY_EVENT as u16 {
+ // This isn't a key event; ignore it.
+ continue;
+ }
+
+ key_event = unsafe { mem::transmute(buffer.Event) };
+
+ if key_event.bKeyDown == 0 {
+ // This is a key being released; ignore it.
+ continue;
+ }
+
+ return Ok(key_event);
+ }
+}
+
+pub fn wants_emoji() -> bool {
+ // If WT_SESSION is set, we can assume we're running in the nne
+ // Windows Terminal. The correct way to detect this is not available
+ // yet. See https://github.com/microsoft/terminal/issues/1040
+ env::var("WT_SESSION").is_ok()
+}
+
+/// Returns true if there is an MSYS tty on the given handle.
+pub fn msys_tty_on(term: &Term) -> bool {
+ let handle = term.as_raw_handle();
+ unsafe {
+ // Check whether the Windows 10 native pty is enabled
+ {
+ let mut out = MaybeUninit::uninit();
+ let res = GetConsoleMode(handle as HANDLE, out.as_mut_ptr());
+ if res != 0 // If res is true then out was initialized.
+ && (out.assume_init() & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ == ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ {
+ return true;
+ }
+ }
+
+ let size = mem::size_of::<FILE_NAME_INFO>();
+ let mut name_info_bytes = vec![0u8; size + MAX_PATH as usize * mem::size_of::<u16>()];
+ let res = GetFileInformationByHandleEx(
+ handle as HANDLE,
+ FileNameInfo,
+ &mut *name_info_bytes as *mut _ as *mut c_void,
+ name_info_bytes.len() as u32,
+ );
+ if res == 0 {
+ return false;
+ }
+ let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
+ let s = slice::from_raw_parts(
+ name_info.FileName.as_ptr(),
+ name_info.FileNameLength as usize / 2,
+ );
+ let name = String::from_utf16_lossy(s);
+ // This checks whether 'pty' exists in the file name, which indicates that
+ // a pseudo-terminal is attached. To mitigate against false positives
+ // (e.g., an actual file name that contains 'pty'), we also require that
+ // either the strings 'msys-' or 'cygwin-' are in the file name as well.)
+ let is_msys = name.contains("msys-") || name.contains("cygwin-");
+ let is_pty = name.contains("-pty");
+ is_msys && is_pty
+ }
+}
+
+pub fn set_title<T: Display>(title: T) {
+ let buffer: Vec<u16> = OsStr::new(&format!("{}", title))
+ .encode_wide()
+ .chain(once(0))
+ .collect();
+ unsafe {
+ SetConsoleTitleW(buffer.as_ptr());
+ }
+}
diff --git a/vendor/console/tests/data/sample_zellij_session.log b/vendor/console/tests/data/sample_zellij_session.log
new file mode 100755
index 0000000..a777258
--- /dev/null
+++ b/vendor/console/tests/data/sample_zellij_session.log
@@ -0,0 +1,56 @@
+% ]2;wintermute@ai:~/Programming/Repos/lib/console]1;..s/lib/console 
+console on  use-handwritten-ansi-parser [!] is 📦 v0.15.0 via 🦀 v1.57.0 
+❯ [?1h=[?2004hnnfnf[?1l>[?2004l
+]2;neofetch]1;nf[?25l[?7l -`
+ .o+`
+ `ooo/
+ `+oooo:
+ `+oooooo:
+ -+oooooo+:
+ `/:-:++oooo+:
+ `/++++/+++++++:
+ `/++++++++++++++:
+ `/+++ooooooooooooo/`
+ ./ooosssso++osssssso+`
+ .oossssso-````/ossssss+`
+ -osssssso. :ssssssso.
+ :osssssss/ osssso+++.
+ /ossssssss/ +ssssooo/-
+ `/ossssso+/:- -:/+osssso+-
+ `+sso+:-` `.-/+oso:
+ `++:. `-/+/
+ .` `/
+wintermute@ai
+-------------
+OS: Arch Linux x86_64
+Host: 82BH Yoga 7 14ITL5
+Kernel: 5.15.12-arch1-1
+Uptime: 6 hours, 14 mins
+Packages: 1397 (pacman)
+Shell: zsh 5.8
+Resolution: 2560x1440
+WM: i3
+Theme: Adwaita [GTK3]
+Icons: Adwaita [GTK3]
+Terminal: zellij
+CPU: 11th Gen Intel i5-1135G7 (8) @ 4.200GHz
+GPU: Intel TigerLake-LP GT2 [Iris Xe Graphics]
+Memory: 6181MiB / 15800MiB
+
+        
+        
+
+
+[?25h[?7h% ]2;wintermute@ai:~/Programming/Repos/lib/console]1;..s/lib/console 
+console on  use-handwritten-ansi-parser [!] is 📦 v0.15.0 via 🦀 v1.57.0 
+❯ [?1h=[?2004h[?2004l
+% ]2;wintermute@ai:~/Programming/Repos/lib/console]1;..s/lib/console 
+console on  use-handwritten-ansi-parser [!] is 📦 v0.15.0 via 🦀 v1.57.0 
+❯ [?1h=[?2004h^jjq rslt.jsonjjq rslt.jsonjjc          cc 
+console on  use-handwritten-ansi-parser [!] is 📦 v0.15.0 via 🦀 v1.57.0 
+❮ jjcc 
+console on  use-handwritten-ansi-parser [!] is 📦 v0.15.0 via 🦀 v1.57.0 
+❯ rrangerrannggeranger[?1l>[?2004l
+]2;. ranger]1;ranger[?1049h(B[?7h[?1h=[?25l[?1006;1000h(Bloading...kranger\[?1h=[?1006;1000hkranger\ (Bwintermute@ai /home/wintermute/Programming/Repos/lib/console/examples  a2s-rs (B=✓(B (B examples 4 (B ... (B console (B (B src9 (B (B fast_rsync (B (B target4 (B (B fax (B (B (BCargo.lock8.1 K (B ghostwriter (B (B (BCargo.toml1.08 K (B lead-oxide (B (B (BCHANGELOG.md1.09 K (B pdf (B (B (BLICENSE1.09 K (B pubproxpy (B (B (BREADME.md1.89 K (B rand-dir  rust-s3  rust-slack  steamlocate-~  subwinder  sysinfo  thing  vdf-rs  zip-extensio~ (Bdrwxr-xr-x 2 wintermute wintermute 4 2022-01-03 00:11 (git: use-handwritten-ansi-parser) 2022-01-05 11:59 13.3K sum, 126G free 1/8 Al[?7ll[?7h(B(B colors.rs (B (Bcolors256.rs(B (Bcursor_at.rs(B (Bterm.rs⌂ 2022-01-05 11:59(B(B⌂+(B>✓=✓=✓=✓>?=✓=✓=✓⌂+=✓=✓=✓⌂?=+=✓(B(B✓(B✓(B+✓·✓·✓✓✓✓✓✓ 2022-01-05 11:59(B(Bsrc examples4 (B✓(B ...(B(B src 9 +(B 95 17:39+2(B(B ansi.rs +(B (Bcommon_term.rs✓(B (Bkb.rs✓(B (Blib.rs✓(B (Bterm.rs✓(B (Bunix_term.rs✓(B (Butils.rs✓(B (Bwasm_term.rs✓(B (Bwindows_term.rs✓(B(Btarget(B...(B(B src9 (B+(B(B target 4 ·(B 442:53·3(B(B debug ·(B release(B·(B (BCACHEDIR.TAG·(B(Btarget/debugexamples (B✓(B (B debug 6 ·(B ...(B (B src (B+(B (B release8 (B·(B (B target ·(B (B (BCACHEDIR.TAG177 B ·(BCargo.lock ·(BCargo.toml ✓(BCHANGELOG.md ✓(BLICENSE ✓(BREADME.md ✓(B 765.061/3(B(B build ·(B deps(B·(B examples(B·(B incremental(B·(B(Brelease debug6 (B·(B ...(B(B release 8 ·(B 812(B(B build ·(B deps(B·(B examples(B·(B incremental(B·(B (Blibconsole.d·(B (Blibconsole.rlib·(B(Brelease/builddebug (B·(B (B build 6 ·(B ...(B (B release ·(B (B deps107 (B·(B (B (BCACHEDIR.TAG ·(B (B examples0 (B (B incremental0 (B·(B (B (Blibconsole.d502 B ·(B (B (Blibconsole.rlib839 K ·(B 863 8401/6(B(B libc-09f1df6a1e55c67a ·(B libc-34b2cd9a598131ff(B·(B libc-1205fcf5a10a7473(B·(B libc-2978e3f83ccd5ba9(B·(B num-traits-4b89db8e22802f0d(B·(B num-traits-b59938ea688eb694(B·(B[?1l>[?12l[?25h[?1006;1000l[?1049l [?1l>% ]2;wintermute@ai:~/Programming/Repos/lib/console/target/release]1;..arget/release 
+console/target/release on  use-handwritten-ansi-parser [!] took 3s
+❯ [?1h=[?2004h \ No newline at end of file