summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1581
-rw-r--r--Cargo.toml40
-rw-r--r--Makefile23
-rw-r--r--deny.toml2
-rw-r--r--src/config.rs25
-rw-r--r--src/dirs.rs20
-rw-r--r--src/env.rs6
-rw-r--r--src/format.rs12
-rw-r--r--src/history.pest5
-rw-r--r--src/info.rs23
-rw-r--r--src/main.rs31
-rw-r--r--src/mutex.rs10
-rw-r--r--src/parse/ast.rs92
-rw-r--r--src/parse/mod.rs (renamed from src/parse.rs)19
-rw-r--r--src/parse/test_ast.rs31
-rw-r--r--src/prelude.rs48
-rw-r--r--src/runner/builtins/command.rs123
-rw-r--r--src/runner/builtins/mod.rs116
-rw-r--r--src/runner/command.rs39
-rw-r--r--src/runner/mod.rs310
-rw-r--r--src/runner/sys.rs79
-rw-r--r--src/shell.pest8
-rw-r--r--src/shell/event.rs135
-rw-r--r--src/shell/history/entry.rs344
-rw-r--r--src/shell/history/mod.rs302
-rw-r--r--src/shell/history/pty.rs244
-rw-r--r--src/shell/inputs/clock.rs27
-rw-r--r--src/shell/inputs/git.rs (renamed from src/shell/git.rs)73
-rw-r--r--src/shell/inputs/mod.rs32
-rw-r--r--src/shell/inputs/signals.rs30
-rw-r--r--src/shell/inputs/stdin.rs17
-rw-r--r--src/shell/mod.rs497
-rw-r--r--src/shell/old_history.rs185
-rw-r--r--src/shell/readline.rs12
34 files changed, 2622 insertions, 1919 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3a004de..dbd3a93 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,19 +3,25 @@
version = 3
[[package]]
-name = "ansi_term"
-version = "0.12.1"
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
- "winapi 0.3.9",
+ "memchr",
]
[[package]]
name = "anyhow"
-version = "1.0.52"
+version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
+checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
[[package]]
name = "arrayvec"
@@ -24,140 +30,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
-name = "async-channel"
-version = "1.6.1"
+name = "async-stream"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625"
dependencies = [
- "concurrent-queue",
- "event-listener",
+ "async-stream-impl",
"futures-core",
]
[[package]]
-name = "async-executor"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
-dependencies = [
- "async-task",
- "concurrent-queue",
- "fastrand",
- "futures-lite",
- "once_cell",
- "slab",
-]
-
-[[package]]
-name = "async-global-executor"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6"
-dependencies = [
- "async-channel",
- "async-executor",
- "async-io",
- "async-mutex",
- "blocking",
- "futures-lite",
- "num_cpus",
- "once_cell",
-]
-
-[[package]]
-name = "async-io"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
-dependencies = [
- "concurrent-queue",
- "futures-lite",
- "libc",
- "log",
- "once_cell",
- "parking",
- "polling",
- "slab",
- "socket2",
- "waker-fn",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "async-lock"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
-dependencies = [
- "event-listener",
-]
-
-[[package]]
-name = "async-mutex"
-version = "1.4.0"
+name = "async-stream-impl"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
dependencies = [
- "event-listener",
-]
-
-[[package]]
-name = "async-process"
-version = "1.3.0"
-source = "git+https://github.com/doy/async-process#5e25598d6fcf3865f2b9e106ba049a26a490a884"
-dependencies = [
- "async-io",
- "blocking",
- "cfg-if 1.0.0",
- "event-listener",
- "futures-lite",
- "libc",
- "once_cell",
- "signal-hook",
- "winapi 0.3.9",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "async-std"
-version = "1.10.0"
+name = "async-trait"
+version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952"
+checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
dependencies = [
- "async-channel",
- "async-global-executor",
- "async-io",
- "async-lock",
- "async-process",
- "crossbeam-utils",
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-lite",
- "gloo-timers",
- "kv-log-macro",
- "log",
- "memchr",
- "num_cpus",
- "once_cell",
- "pin-project-lite",
- "pin-utils",
- "slab",
- "wasm-bindgen-futures",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "async-task"
-version = "4.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
-
-[[package]]
-name = "atomic-waker"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
-
-[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -165,14 +69,20 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
- "winapi 0.3.9",
+ "winapi",
]
[[package]]
name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bincode"
@@ -211,26 +121,6 @@ dependencies = [
]
[[package]]
-name = "blocking"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427"
-dependencies = [
- "async-channel",
- "async-task",
- "atomic-waker",
- "fastrand",
- "futures-lite",
- "once_cell",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
-
-[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -243,75 +133,121 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
-name = "cache-padded"
-version = "1.2.0"
+name = "bytes"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
-version = "1.0.72"
+version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
[[package]]
name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
-version = "2.34.0"
+version = "3.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "ced1892c55c910c1219e98d6fc8d71f6bddba7905866ce740066d8bfea859312"
dependencies = [
- "ansi_term",
"atty",
"bitflags",
+ "clap_derive",
+ "indexmap",
+ "lazy_static",
+ "os_str_bytes",
"strsim",
- "term_size",
+ "termcolor",
+ "terminal_size",
"textwrap",
- "unicode-width",
- "vec_map",
]
[[package]]
-name = "concurrent-queue"
-version = "1.2.2"
+name = "clap_derive"
+version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
dependencies = [
- "cache-padded",
+ "heck 0.4.0",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "crossbeam-utils"
-version = "0.8.6"
+name = "console-api"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
+checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a"
dependencies = [
- "cfg-if 1.0.0",
- "lazy_static",
+ "prost",
+ "prost-types",
+ "tonic",
+ "tonic-build",
+ "tracing-core",
]
[[package]]
-name = "ctor"
-version = "0.1.21"
+name = "console-subscriber"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
+checksum = "565a7dfea2d10dd0e5c57cc394d5d441b1910960d8c9211ed14135e0e6ec3a20"
dependencies = [
- "quote",
- "syn",
+ "console-api",
+ "crossbeam-channel",
+ "crossbeam-utils",
+ "futures",
+ "hdrhistogram",
+ "humantime",
+ "prost-types",
+ "serde",
+ "serde_json",
+ "thread_local",
+ "tokio",
+ "tokio-stream",
+ "tonic",
+ "tracing",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
+dependencies = [
+ "cfg-if",
+ "lazy_static",
]
[[package]]
@@ -324,10 +260,30 @@ dependencies = [
]
[[package]]
-name = "event-listener"
-version = "2.5.1"
+name = "directories"
+version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
+checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "fake-simd"
@@ -337,9 +293,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastrand"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
@@ -350,98 +306,96 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"redox_syscall",
- "winapi 0.3.9",
+ "winapi",
]
[[package]]
-name = "form_urlencoded"
-version = "1.0.1"
+name = "fixedbitset"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
+
+[[package]]
+name = "flate2"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
- "matches",
- "percent-encoding",
+ "cfg-if",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
]
[[package]]
-name = "fsevent"
-version = "0.4.0"
+name = "fnv"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
- "bitflags",
- "fsevent-sys",
+ "matches",
+ "percent-encoding",
]
[[package]]
name = "fsevent-sys"
-version = "2.0.1"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
+checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
+name = "futures"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
- "bitflags",
- "fuchsia-zircon-sys",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
]
[[package]]
-name = "fuchsia-zircon-sys"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-
-[[package]]
name = "futures-channel"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
name = "futures-core"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-io"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
-
-[[package]]
-name = "futures-lite"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
-dependencies = [
- "fastrand",
- "futures-core",
- "futures-io",
- "memchr",
- "parking",
- "pin-project-lite",
- "waker-fn",
-]
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
@@ -449,19 +403,26 @@ dependencies = [
]
[[package]]
+name = "futures-sink"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+
+[[package]]
name = "futures-task"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
-version = "0.3.19"
+version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-macro",
+ "futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
@@ -478,17 +439,26 @@ dependencies = [
]
[[package]]
+name = "getrandom"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
name = "git2"
-version = "0.13.25"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6"
+checksum = "6e7d3b96ec1fcaa8431cf04a4f1ef5caafe58d5cf7bcc31f09c1626adddb0ffe"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
- "openssl-probe",
- "openssl-sys",
"url",
]
@@ -499,16 +469,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
-name = "gloo-timers"
-version = "0.2.2"
+name = "h2"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e"
+checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e"
dependencies = [
- "futures-channel",
+ "bytes",
+ "fnv",
"futures-core",
- "js-sys",
- "wasm-bindgen",
- "web-sys",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util 0.6.9",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "hdrhistogram"
+version = "7.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0"
+dependencies = [
+ "base64",
+ "byteorder",
+ "flate2",
+ "nom",
+ "num-traits",
]
[[package]]
@@ -521,6 +516,12 @@ dependencies = [
]
[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -537,7 +538,83 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
"match_cfg",
- "winapi 0.3.9",
+ "winapi",
+]
+
+[[package]]
+name = "http"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "hyper"
+version = "0.14.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
]
[[package]]
@@ -552,10 +629,20 @@ dependencies = [
]
[[package]]
+name = "indexmap"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
name = "inotify"
-version = "0.7.1"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
+checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
"bitflags",
"inotify-sys",
@@ -577,26 +664,20 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
-name = "iovec"
-version = "0.1.4"
+name = "itertools"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
- "libc",
+ "either",
]
[[package]]
name = "itoa"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
-
-[[package]]
-name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
@@ -611,31 +692,23 @@ dependencies = [
]
[[package]]
-name = "js-sys"
-version = "0.3.55"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
+name = "kqueue"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+checksum = "058a107a784f8be94c7d35c1300f4facced2e93d2fbe5b1452b44e905ddca4a9"
dependencies = [
- "winapi 0.2.8",
- "winapi-build",
+ "kqueue-sys",
+ "libc",
]
[[package]]
-name = "kv-log-macro"
-version = "1.0.7"
+name = "kqueue-sys"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
dependencies = [
- "log",
+ "bitflags",
+ "libc",
]
[[package]]
@@ -645,55 +718,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
-name = "lazycell"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
-
-[[package]]
name = "libc"
-version = "0.2.112"
+version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libgit2-sys"
-version = "0.12.26+1.3.0"
+version = "0.13.1+1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494"
+checksum = "43e598aa7a4faedf1ea1b4608f582b06f0f40211eec551b7ef36019ae3f62def"
dependencies = [
"cc",
"libc",
- "libssh2-sys",
"libz-sys",
- "openssl-sys",
"pkg-config",
]
[[package]]
-name = "libssh2-sys"
-version = "0.2.23"
+name = "libz-sys"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
+checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
dependencies = [
"cc",
"libc",
- "libz-sys",
- "openssl-sys",
"pkg-config",
"vcpkg",
]
[[package]]
-name = "libz-sys"
-version = "1.1.3"
+name = "lock_api"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
+ "scopeguard",
]
[[package]]
@@ -702,8 +762,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
- "cfg-if 1.0.0",
- "value-bag",
+ "cfg-if",
]
[[package]]
@@ -740,57 +799,59 @@ dependencies = [
]
[[package]]
-name = "mio"
-version = "0.6.23"
+name = "minimal-lexical"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
- "cfg-if 0.1.10",
- "fuchsia-zircon",
- "fuchsia-zircon-sys",
- "iovec",
- "kernel32-sys",
- "libc",
- "log",
- "miow",
- "net2",
- "slab",
- "winapi 0.2.8",
+ "adler",
+ "autocfg",
]
[[package]]
-name = "mio-extras"
-version = "2.0.6"
+name = "mio"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
+checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
dependencies = [
- "lazycell",
+ "libc",
"log",
- "mio",
- "slab",
+ "miow",
+ "ntapi",
+ "winapi",
]
[[package]]
name = "miow"
-version = "0.2.2"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
- "kernel32-sys",
- "net2",
- "winapi 0.2.8",
- "ws2_32-sys",
+ "winapi",
]
[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
name = "nbsh"
version = "0.1.0"
dependencies = [
"anyhow",
- "async-std",
"bincode",
- "blocking",
- "futures-lite",
+ "bytes",
+ "clap",
+ "console-subscriber",
+ "directories",
"futures-util",
"git2",
"glob",
@@ -799,61 +860,77 @@ dependencies = [
"nix",
"notify",
"once_cell",
- "paw",
"pest",
"pest_derive",
"pty-process",
"serde",
- "signal-hook-async-std",
- "structopt",
"terminal_size",
"textmode",
"time",
+ "tokio",
+ "tokio-stream",
+ "tokio-util 0.7.0",
+ "toml",
"unicode-width",
"users",
"vt100",
]
[[package]]
-name = "net2"
-version = "0.2.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+name = "nix"
+version = "0.23.1"
+source = "git+https://github.com/nix-rust/nix#9312f1c410e7390f9ccb9ea8f255e09b4bb2a0ee"
dependencies = [
- "cfg-if 0.1.10",
+ "bitflags",
+ "cfg-if",
"libc",
- "winapi 0.3.9",
+ "memoffset",
]
[[package]]
-name = "nix"
-version = "0.23.1"
+name = "nom"
+version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
+checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
dependencies = [
- "bitflags",
- "cc",
- "cfg-if 1.0.0",
- "libc",
- "memoffset",
+ "memchr",
+ "minimal-lexical",
+ "version_check",
]
[[package]]
name = "notify"
-version = "4.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
+version = "5.0.0-pre.13"
+source = "git+https://github.com/notify-rs/notify#b1802ecc8051b5c890ae05483513c75a71834f93"
dependencies = [
"bitflags",
+ "crossbeam-channel",
"filetime",
- "fsevent",
"fsevent-sys",
"inotify",
+ "kqueue",
"libc",
"mio",
- "mio-extras",
"walkdir",
- "winapi 0.3.9",
+ "winapi",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+dependencies = [
+ "autocfg",
]
[[package]]
@@ -867,10 +944,19 @@ dependencies = [
]
[[package]]
+name = "num_threads"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "once_cell"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "opaque-debug"
@@ -879,58 +965,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
-name = "openssl-probe"
-version = "0.1.5"
+name = "os_str_bytes"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
-
-[[package]]
-name = "openssl-sys"
-version = "0.9.72"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
- "autocfg",
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
+ "memchr",
]
[[package]]
-name = "parking"
-version = "2.0.0"
+name = "parking_lot"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
-
-[[package]]
-name = "paw"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09c0fc9b564dbc3dc2ed7c92c0c144f4de340aa94514ce2b446065417c4084e9"
+checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
- "paw-attributes",
- "paw-raw",
+ "lock_api",
+ "parking_lot_core",
]
[[package]]
-name = "paw-attributes"
-version = "1.0.2"
+name = "parking_lot_core"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f35583365be5d148e959284f42526841917b7bfa09e2d1a7ad5dde2cf0eaa39"
+checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
]
[[package]]
-name = "paw-raw"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0b59668fe80c5afe998f0c0bf93322bf2cd66cafeeb80581f291716f3467f2"
-
-[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -980,6 +1046,36 @@ dependencies = [
]
[[package]]
+name = "petgraph"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -998,17 +1094,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
-name = "polling"
-version = "2.2.0"
+name = "ppv-lite86"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "log",
- "wepoll-ffi",
- "winapi 0.3.9",
-]
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
@@ -1044,36 +1133,159 @@ dependencies = [
]
[[package]]
+name = "prost"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5"
+dependencies = [
+ "bytes",
+ "heck 0.3.3",
+ "itertools",
+ "lazy_static",
+ "log",
+ "multimap",
+ "petgraph",
+ "prost",
+ "prost-types",
+ "regex",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
+dependencies = [
+ "bytes",
+ "prost",
+]
+
+[[package]]
name = "pty-process"
version = "0.2.0"
-source = "git+https://github.com/doy/pty-process#ebcf5f15081f6a84c861eb2aecbf962396a88695"
+source = "git+https://github.com/doy/pty-process#b2733e64d900ac237211360848326f3c4caa23f5"
dependencies = [
- "async-io",
- "async-process",
- "futures-io",
"libc",
"nix",
+ "tokio",
]
[[package]]
name = "quote"
-version = "1.0.14"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
+checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[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.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
dependencies = [
"bitflags",
]
[[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+
+[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1083,19 +1295,25 @@ dependencies = [
]
[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
name = "serde"
-version = "1.0.133"
+version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
+checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.133"
+version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [
"proc-macro2",
"quote",
@@ -1103,6 +1321,17 @@ dependencies = [
]
[[package]]
+name = "serde_json"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1115,25 +1344,12 @@ dependencies = [
]
[[package]]
-name = "signal-hook"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
-dependencies = [
- "libc",
- "signal-hook-registry",
-]
-
-[[package]]
-name = "signal-hook-async-std"
-version = "0.2.2"
+name = "sharded-slab"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
- "async-io",
- "futures-lite",
- "libc",
- "signal-hook",
+ "lazy_static",
]
[[package]]
@@ -1152,65 +1368,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
+name = "smallvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+
+[[package]]
name = "socket2"
-version = "0.4.2"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
- "winapi 0.3.9",
+ "winapi",
]
[[package]]
name = "strsim"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
-[[package]]
-name = "structopt"
-version = "0.3.25"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
-dependencies = [
- "clap",
- "lazy_static",
- "paw",
- "structopt-derive",
-]
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
-name = "structopt-derive"
-version = "0.4.18"
+name = "syn"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
- "heck",
- "proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "unicode-xid",
]
[[package]]
-name = "syn"
-version = "1.0.85"
+name = "tempfile"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
]
[[package]]
-name = "term_size"
-version = "0.3.2"
+name = "termcolor"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
- "libc",
- "winapi 0.3.9",
+ "winapi-util",
]
[[package]]
@@ -1220,44 +1430,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
- "winapi 0.3.9",
+ "winapi",
]
[[package]]
name = "textmode"
version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf9ecdb23aae465624a900ae8c795e17f46e081b8454f8ea5b3b5c27a9e7884"
+source = "git+https://github.com/doy/textmode#193e1963afc4e9e78122573cd5b9831f9a847345"
dependencies = [
- "blocking",
- "futures-lite",
- "itoa 1.0.1",
+ "itoa",
"nix",
"terminal_size",
+ "tokio",
"vt100",
]
[[package]]
name = "textwrap"
-version = "0.11.0"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
dependencies = [
- "term_size",
- "unicode-width",
+ "terminal_size",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+dependencies = [
+ "once_cell",
]
[[package]]
name = "time"
-version = "0.3.5"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
+checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
dependencies = [
- "itoa 0.4.8",
+ "itoa",
"libc",
+ "num_threads",
+ "time-macros",
]
[[package]]
+name = "time-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
+
+[[package]]
name = "tinyvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1273,6 +1497,232 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
+name = "tokio"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+dependencies = [
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "once_cell",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "tracing",
+ "winapi",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "tonic"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "prost-derive",
+ "tokio",
+ "tokio-stream",
+ "tokio-util 0.6.9",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+ "tracing-futures",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757"
+dependencies = [
+ "proc-macro2",
+ "prost-build",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util 0.7.0",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
+
+[[package]]
+name = "tower-service"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
+
+[[package]]
+name = "tracing"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6c650a8ef0cd2dd93736f033d21cbd1224c5a967aa0c258d00fcf7dafef9b9f"
+dependencies = [
+ "cfg-if",
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
+dependencies = [
+ "lazy_static",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
+dependencies = [
+ "sharded-slab",
+ "thread_local",
+ "tracing-core",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+
+[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1301,9 +1751,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
-version = "1.8.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
@@ -1346,14 +1796,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
-name = "value-bag"
-version = "1.0.0-alpha.8"
+name = "valuable"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
-dependencies = [
- "ctor",
- "version_check",
-]
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
@@ -1362,12 +1808,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1379,7 +1819,7 @@ version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7541312ce0411d878458abf25175d878e8edc38f9f12ee8eed1d65870cacf540"
dependencies = [
- "itoa 1.0.1",
+ "itoa",
"log",
"unicode-width",
"vte",
@@ -1407,156 +1847,113 @@ dependencies = [
]
[[package]]
-name = "waker-fn"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
-
-[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
- "winapi 0.3.9",
+ "winapi",
"winapi-util",
]
[[package]]
-name = "wasm-bindgen"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
-dependencies = [
- "cfg-if 1.0.0",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.78"
+name = "want"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
- "bumpalo",
- "lazy_static",
"log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
+ "try-lock",
]
[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.28"
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
-dependencies = [
- "cfg-if 1.0.0",
- "js-sys",
- "wasm-bindgen",
- "web-sys",
-]
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.78"
+name = "which"
+version = "4.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
+checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2"
dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
+ "either",
+ "lazy_static",
+ "libc",
]
[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.78"
+name = "winapi"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
]
[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
-
-[[package]]
-name = "web-sys"
-version = "0.3.55"
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
-name = "wepoll-ffi"
-version = "0.1.2"
+name = "winapi-util"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
- "cc",
+ "winapi",
]
[[package]]
-name = "winapi"
-version = "0.2.8"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
-name = "winapi"
-version = "0.3.9"
+name = "windows-sys"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
]
[[package]]
-name = "winapi-build"
-version = "0.1.1"
+name = "windows_aarch64_msvc"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_i686_gnu"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
-name = "winapi-util"
-version = "0.1.5"
+name = "windows_i686_msvc"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi 0.3.9",
-]
+checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_x86_64_gnu"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
-name = "ws2_32-sys"
-version = "0.2.1"
+name = "windows_x86_64_msvc"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
+checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
diff --git a/Cargo.toml b/Cargo.toml
index cb0cdcc..89b201e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,34 +6,42 @@ edition = "2021"
license = "MIT"
[dependencies]
-anyhow = "1.0.52"
-async-std = { version = "1.10.0", features = ["unstable"] }
+anyhow = "1.0.55"
bincode = "1.3.3"
-blocking = "1.1.0"
-futures-lite = "1.12.0"
-futures-util = "0.3.19"
-git2 = "0.13.25"
+bytes = "1.1.0"
+clap = { version = "3.1.5", features = ["wrap_help", "derive"] }
+directories = "4.0.1"
+futures-util = "0.3.21"
+git2 = { version = "0.14.1", default-features = false }
glob = "0.3.0"
hostname = "0.3.1"
-libc = "0.2.112"
+libc = "0.2.119"
nix = "0.23.1"
-notify = "4.0.17"
-once_cell = "1.9.0"
-paw = "1.0.0"
+notify = "5.0.0-pre.13"
+once_cell = "1.10.0"
pest = "2.1.3"
pest_derive = "2.1.0"
pty-process = { version = "0.2.0", features = ["async"] }
-serde = { version = "1.0.133", features = ["derive"] }
-signal-hook-async-std = "0.2.2"
-structopt = { version = "0.3.25", features = ["paw", "wrap_help"] }
+serde = { version = "1.0.136", features = ["derive"] }
terminal_size = "0.1.17"
textmode = { version = "0.3.0", features = ["async"] }
-time = { version = "0.3.5", features = ["formatting", "parsing"] }
+time = { version = "0.3.7", features = ["formatting", "parsing"] }
+tokio = { version = "1.17.0", features = ["full"] }
+tokio-stream = { version = "0.1.8", features = ["io-util"] }
+tokio-util = { version = "0.7.0", features = ["io"] }
+toml = "0.5.8"
unicode-width = "0.1.9"
users = "0.11.0"
vt100 = "0.15.1"
+[target.'cfg(nbsh_tokio_console)'.dependencies]
+console-subscriber = "0.1.3"
+
[patch.crates-io]
-# https://github.com/smol-rs/async-process/pull/19
-async-process = { git = "https://github.com/doy/async-process" }
+nix = { git = "https://github.com/nix-rust/nix" }
+notify = { git = "https://github.com/notify-rs/notify" }
pty-process = { git = "https://github.com/doy/pty-process" }
+textmode = { git = "https://github.com/doy/textmode" }
+
+[dev-dependencies]
+time = { version = "0.3.7", features = ["macros"] }
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f5641f2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+build:
+ cargo build
+.PHONY: build
+
+run:
+ cargo run
+.PHONY: run
+
+console:
+ RUSTFLAGS="--cfg tokio_unstable --cfg nbsh_tokio_console" cargo run
+.PHONY: console
+
+release:
+ cargo build --release
+.PHONY: release
+
+run-release:
+ cargo run --release
+.PHONY: run-release
+
+console-release:
+ RUSTFLAGS="--cfg tokio_unstable --cfg nbsh_tokio_console" cargo run --release
+.PHONY: console-release
diff --git a/deny.toml b/deny.toml
index 5b7ebc5..90446c7 100644
--- a/deny.toml
+++ b/deny.toml
@@ -10,5 +10,5 @@ unsound = "deny"
[bans]
[licenses]
-allow = ["MIT", "Apache-2.0"]
+allow = ["MIT", "Apache-2.0", "ISC", "CC0-1.0"]
copyleft = "deny"
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..08fa002
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,25 @@
+use crate::prelude::*;
+
+#[derive(serde::Deserialize, Default, Debug)]
+pub struct Config {
+ aliases:
+ std::collections::HashMap<std::path::PathBuf, crate::parse::ast::Exe>,
+}
+
+impl Config {
+ pub fn load() -> Result<Self> {
+ let file = crate::dirs::config_file();
+ if std::fs::metadata(&file).is_ok() {
+ Ok(toml::from_slice(&std::fs::read(&file)?)?)
+ } else {
+ Ok(Self::default())
+ }
+ }
+
+ pub fn alias_for(
+ &self,
+ path: &std::path::Path,
+ ) -> Option<&crate::parse::ast::Exe> {
+ self.aliases.get(path)
+ }
+}
diff --git a/src/dirs.rs b/src/dirs.rs
new file mode 100644
index 0000000..2ffbb33
--- /dev/null
+++ b/src/dirs.rs
@@ -0,0 +1,20 @@
+static PROJECT_DIRS: once_cell::sync::Lazy<directories::ProjectDirs> =
+ once_cell::sync::Lazy::new(|| {
+ directories::ProjectDirs::from("", "", "nbsh").unwrap()
+ });
+
+pub fn config_file() -> std::path::PathBuf {
+ config_dir().join("config.toml")
+}
+
+pub fn history_file() -> std::path::PathBuf {
+ data_dir().join("history")
+}
+
+fn config_dir() -> std::path::PathBuf {
+ PROJECT_DIRS.config_dir().to_path_buf()
+}
+
+fn data_dir() -> std::path::PathBuf {
+ PROJECT_DIRS.data_dir().to_path_buf()
+}
diff --git a/src/env.rs b/src/env.rs
index 3a6d8b5..72a69d1 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -16,7 +16,7 @@ const __NBSH_LATEST_STATUS: &str = "__NBSH_LATEST_STATUS";
const __NBSH_PREV_PWD: &str = "__NBSH_PREV_PWD";
impl Env {
- pub fn new() -> anyhow::Result<Self> {
+ pub fn new() -> Result<Self> {
let pwd = std::env::current_dir()?;
Ok(Self::V0(V0 {
pwd: pwd.clone(),
@@ -26,7 +26,7 @@ impl Env {
}))
}
- pub fn new_from_env() -> anyhow::Result<Self> {
+ pub fn new_from_env() -> Result<Self> {
let pwd = std::env::current_dir()?;
Ok(Self::V0(V0 {
pwd: pwd.clone(),
@@ -111,7 +111,7 @@ impl Env {
}
}
- pub fn update(&mut self) -> anyhow::Result<()> {
+ pub fn update(&mut self) -> Result<()> {
let idx = self.idx();
let status = self.latest_status();
let prev_pwd = self.prev_pwd();
diff --git a/src/format.rs b/src/format.rs
index 55757c4..115ee6c 100644
--- a/src/format.rs
+++ b/src/format.rs
@@ -23,8 +23,16 @@ pub fn exit_status(status: std::process::ExitStatus) -> String {
}
pub fn time(time: time::OffsetDateTime) -> String {
- let format =
- time::format_description::parse("[hour]:[minute]:[second]").unwrap();
+ let format = if time::OffsetDateTime::now_utc() - time
+ > std::time::Duration::from_secs(60 * 60 * 24)
+ {
+ time::format_description::parse(
+ "[year]-[month]-[day] [hour]:[minute]:[second]",
+ )
+ .unwrap()
+ } else {
+ time::format_description::parse("[hour]:[minute]:[second]").unwrap()
+ };
time.format(&format).unwrap()
}
diff --git a/src/history.pest b/src/history.pest
new file mode 100644
index 0000000..67597d1
--- /dev/null
+++ b/src/history.pest
@@ -0,0 +1,5 @@
+time = @{ ASCII_DIGIT+ }
+duration = @{ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }
+command = @{ ANY* }
+
+line = ${ SOI ~ (": " ~ time ~ ":" ~ duration ~ ";")? ~ command ~ "\n"? ~ EOI }
diff --git a/src/info.rs b/src/info.rs
index bd94205..6a5ad4f 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -1,12 +1,14 @@
-pub fn user() -> anyhow::Result<String> {
+use crate::prelude::*;
+
+pub fn user() -> Result<String> {
Ok(users::get_current_username()
- .ok_or_else(|| anyhow::anyhow!("couldn't get username"))?
+ .ok_or_else(|| anyhow!("couldn't get username"))?
.to_string_lossy()
.into_owned())
}
#[allow(clippy::unnecessary_wraps)]
-pub fn prompt_char() -> anyhow::Result<String> {
+pub fn prompt_char() -> Result<String> {
if users::get_current_uid() == 0 {
Ok("#".into())
} else {
@@ -14,7 +16,7 @@ pub fn prompt_char() -> anyhow::Result<String> {
}
}
-pub fn hostname() -> anyhow::Result<String> {
+pub fn hostname() -> Result<String> {
let mut hostname = hostname::get()?.to_string_lossy().into_owned();
if let Some(idx) = hostname.find('.') {
hostname.truncate(idx);
@@ -23,7 +25,7 @@ pub fn hostname() -> anyhow::Result<String> {
}
#[allow(clippy::unnecessary_wraps)]
-pub fn time(offset: time::UtcOffset) -> anyhow::Result<String> {
+pub fn time(offset: time::UtcOffset) -> Result<String> {
Ok(crate::format::time(
time::OffsetDateTime::now_utc().to_offset(offset),
))
@@ -33,6 +35,17 @@ pub fn pid() -> String {
nix::unistd::getpid().to_string()
}
+#[cfg(target_os = "linux")]
+#[allow(clippy::unnecessary_wraps)]
+pub fn current_exe() -> Result<std::path::PathBuf> {
+ Ok("/proc/self/exe".into())
+}
+
+#[cfg(not(target_os = "linux"))]
+pub fn current_exe() -> Result<std::path::PathBuf> {
+ Ok(std::env::current_exe()?)
+}
+
// the time crate is currently unable to get the local offset on unix due to
// soundness concerns, so we have to do it manually/:
//
diff --git a/src/main.rs b/src/main.rs
index a7d3f4b..d6b2725 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@
#![warn(clippy::get_unwrap)]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::missing_const_for_fn)]
+#![allow(clippy::option_option)]
#![allow(clippy::similar_names)]
#![allow(clippy::struct_excessive_bools)]
#![allow(clippy::too_many_arguments)]
@@ -15,10 +16,11 @@
// just get a compilation failure
#![allow(clippy::future_not_send)]
+mod config;
+mod dirs;
mod env;
mod format;
mod info;
-mod mutex;
mod parse;
mod prelude;
mod runner;
@@ -26,35 +28,40 @@ mod shell;
use prelude::*;
-#[derive(structopt::StructOpt)]
-#[structopt(about = "NoteBook SHell")]
+use clap::Parser as _;
+
+#[derive(clap::Parser)]
+#[clap(about = "NoteBook SHell")]
struct Opt {
- #[structopt(short = "c")]
+ #[clap(short = 'c')]
command: Option<String>,
- #[structopt(long)]
+ #[clap(long)]
status_fd: Option<std::os::unix::io::RawFd>,
}
-async fn async_main(opt: Opt) -> anyhow::Result<i32> {
+#[tokio::main]
+async fn async_main(opt: Opt) -> Result<i32> {
if let Some(command) = opt.command {
- let shell_write = opt.status_fd.and_then(|fd| {
+ let mut shell_write = opt.status_fd.and_then(|fd| {
nix::sys::stat::fstat(fd).ok().map(|_| {
// Safety: we don't create File instances for or read/write
// data on this fd anywhere else
- unsafe { async_std::fs::File::from_raw_fd(fd) }
+ unsafe { tokio::fs::File::from_raw_fd(fd) }
})
});
- return runner::run(&command, shell_write.as_ref()).await;
+ return runner::main(command, &mut shell_write).await;
}
+ #[cfg(nbsh_tokio_console)]
+ console_subscriber::init();
+
shell::main().await
}
-#[paw::main]
-fn main(opt: Opt) {
- match async_std::task::block_on(async_main(opt)) {
+fn main() {
+ match async_main(Opt::parse()) {
Ok(code) => {
std::process::exit(code);
}
diff --git a/src/mutex.rs b/src/mutex.rs
deleted file mode 100644
index df28ffc..0000000
--- a/src/mutex.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub type Mutex<T> = async_std::sync::Arc<async_std::sync::Mutex<T>>;
-pub type Guard<T> = async_std::sync::MutexGuardArc<T>;
-
-pub fn new<T>(t: T) -> async_std::sync::Arc<async_std::sync::Mutex<T>> {
- async_std::sync::Arc::new(async_std::sync::Mutex::new(t))
-}
-
-pub fn clone<T>(m: &Mutex<T>) -> Mutex<T> {
- async_std::sync::Arc::clone(m)
-}
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index e2d5840..5bceed5 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -15,7 +15,7 @@ impl Commands {
pub fn parse(full_cmd: &str) -> Result<Self, super::Error> {
Ok(Self::build_ast(
Shell::parse(Rule::line, full_cmd)
- .map_err(|e| super::Error::new(full_cmd, e))?
+ .map_err(|e| super::Error::new(full_cmd.to_string(), e))?
.next()
.unwrap()
.into_inner()
@@ -90,14 +90,14 @@ pub struct Pipeline {
}
impl Pipeline {
- pub async fn eval(self, env: &Env) -> anyhow::Result<super::Pipeline> {
+ pub async fn eval(self, env: &Env) -> Result<super::Pipeline> {
Ok(super::Pipeline {
exes: self
.exes
.into_iter()
.map(|exe| exe.eval(env))
.collect::<futures_util::stream::FuturesOrdered<_>>()
- .collect::<Result<_, _>>()
+ .try_collect()
.await?,
})
}
@@ -117,14 +117,14 @@ impl Pipeline {
}
#[derive(Debug, Clone, PartialEq, Eq)]
-struct Exe {
+pub struct Exe {
exe: Word,
args: Vec<Word>,
redirects: Vec<Redirect>,
}
impl Exe {
- async fn eval(self, env: &Env) -> anyhow::Result<super::Exe> {
+ pub async fn eval(self, env: &Env) -> Result<super::Exe> {
let exe = self.exe.eval(env).await?;
assert_eq!(exe.len(), 1); // TODO
let exe = &exe[0];
@@ -137,7 +137,7 @@ impl Exe {
arg.eval(env).await.map(IntoIterator::into_iter)
})
.collect::<futures_util::stream::FuturesOrdered<_>>()
- .collect::<Result<Vec<_>, _>>()
+ .try_collect::<Vec<_>>()
.await?
.into_iter()
.flatten()
@@ -147,11 +147,20 @@ impl Exe {
.into_iter()
.map(|arg| arg.eval(env))
.collect::<futures_util::stream::FuturesOrdered<_>>()
- .collect::<Result<_, _>>()
+ .try_collect()
.await?,
})
}
+ pub fn parse(s: &str) -> Result<Self, super::Error> {
+ Ok(Self::build_ast(
+ Shell::parse(Rule::exe, s)
+ .map_err(|e| super::Error::new(s.to_string(), e))?
+ .next()
+ .unwrap(),
+ ))
+ }
+
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
assert!(matches!(pair.as_rule(), Rule::subshell | Rule::exe));
if matches!(pair.as_rule(), Rule::subshell) {
@@ -162,7 +171,7 @@ impl Exe {
return Self {
exe: Word {
parts: vec![WordPart::SingleQuoted(
- std::env::current_exe()
+ crate::info::current_exe()
.unwrap()
.to_str()
.unwrap()
@@ -206,13 +215,43 @@ impl Exe {
}
}
+impl<'de> serde::Deserialize<'de> for Exe {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> serde::de::Visitor<'de> for Visitor {
+ type Value = Exe;
+
+ fn expecting(
+ &self,
+ f: &mut std::fmt::Formatter,
+ ) -> std::fmt::Result {
+ f.write_str("a command")
+ }
+
+ fn visit_str<E>(
+ self,
+ value: &str,
+ ) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Exe::parse(value).map_err(serde::de::Error::custom)
+ }
+ }
+ deserializer.deserialize_string(Visitor)
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Word {
parts: Vec<WordPart>,
}
impl Word {
- pub async fn eval(self, env: &Env) -> anyhow::Result<Vec<String>> {
+ pub async fn eval(self, env: &Env) -> Result<Vec<String>> {
let mut opts = glob::MatchOptions::new();
opts.require_literal_separator = true;
opts.require_literal_leading_dot = true;
@@ -330,12 +369,12 @@ impl WordPart {
match self {
Self::Alternation(_) => unreachable!(),
Self::Substitution(commands) => {
- let mut cmd = async_std::process::Command::new(
- std::env::current_exe().unwrap(),
+ let mut cmd = tokio::process::Command::new(
+ crate::info::current_exe().unwrap(),
);
cmd.args(&["-c", &commands]);
- cmd.stdin(async_std::process::Stdio::inherit());
- cmd.stderr(async_std::process::Stdio::inherit());
+ cmd.stdin(std::process::Stdio::inherit());
+ cmd.stderr(std::process::Stdio::inherit());
let mut out =
String::from_utf8(cmd.output().await.unwrap().stdout)
.unwrap();
@@ -408,15 +447,20 @@ impl Redirect {
let mut iter = pair.into_inner();
let prefix = iter.next().unwrap().as_str();
- let (from, dir) = if let Some(from) = prefix.strip_suffix(">>") {
- (from, super::Direction::Append)
- } else if let Some(from) = prefix.strip_suffix('>') {
- (from, super::Direction::Out)
- } else if let Some(from) = prefix.strip_suffix('<') {
- (from, super::Direction::In)
- } else {
- unreachable!()
- };
+ let (from, dir) = prefix.strip_suffix(">>").map_or_else(
+ || {
+ prefix.strip_suffix('>').map_or_else(
+ || {
+ (
+ prefix.strip_suffix('<').unwrap(),
+ super::Direction::In,
+ )
+ },
+ |from| (from, super::Direction::Out),
+ )
+ },
+ |from| (from, super::Direction::Append),
+ );
let from = if from.is_empty() {
match dir {
super::Direction::In => 0,
@@ -431,7 +475,7 @@ impl Redirect {
Self { from, to, dir }
}
- async fn eval(self, env: &Env) -> anyhow::Result<super::Redirect> {
+ async fn eval(self, env: &Env) -> Result<super::Redirect> {
let to = if self.to.parts.len() == 1 {
if let WordPart::Bareword(s) = &self.to.parts[0] {
if let Some(fd) = s.strip_prefix('&') {
@@ -509,7 +553,7 @@ fn parse_fd(s: &str) -> std::os::unix::io::RawFd {
}
}
-fn expand_home(dir: &str) -> anyhow::Result<String> {
+fn expand_home(dir: &str) -> Result<String> {
if dir.starts_with('~') {
let path: std::path::PathBuf = dir.into();
if let std::path::Component::Normal(prefix) =
diff --git a/src/parse.rs b/src/parse/mod.rs
index cc6d92b..e2b7ec0 100644
--- a/src/parse.rs
+++ b/src/parse/mod.rs
@@ -11,7 +11,7 @@ impl Pipeline {
}
}
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Exe {
exe: std::path::PathBuf,
args: Vec<String>,
@@ -27,6 +27,16 @@ impl Exe {
&self.args
}
+ pub fn append(&mut self, other: Self) {
+ let Self {
+ exe: _exe,
+ args,
+ redirects,
+ } = other;
+ self.args.extend(args);
+ self.redirects.extend(redirects);
+ }
+
pub fn redirects(&self) -> &[Redirect] {
&self.redirects
}
@@ -106,11 +116,8 @@ pub struct Error {
}
impl Error {
- fn new(input: &str, e: pest::error::Error<ast::Rule>) -> Self {
- Self {
- input: input.to_string(),
- e,
- }
+ fn new(input: String, e: pest::error::Error<ast::Rule>) -> Self {
+ Self { input, e }
}
}
diff --git a/src/parse/test_ast.rs b/src/parse/test_ast.rs
index 09d772b..a1f83dd 100644
--- a/src/parse/test_ast.rs
+++ b/src/parse/test_ast.rs
@@ -157,13 +157,23 @@ macro_rules! eval_eq {
_ => continue,
};
assert_eq!(
- async_std::task::block_on(pipeline.eval(&$env)).unwrap(),
+ pipeline.eval(&$env).await.unwrap(),
expected.remove(0)
);
}
}};
}
+macro_rules! deserialize_eq {
+ ($line:literal, $parsed:expr) => {{
+ use serde::de::IntoDeserializer as _;
+ use serde::Deserialize as _;
+ let exe: Result<_, serde::de::value::Error> =
+ Exe::deserialize($line.into_deserializer());
+ assert_eq!(exe.unwrap(), $parsed);
+ }};
+}
+
macro_rules! eval_fails {
($line:literal, $env:expr) => {{
let ast = Commands::parse($line).unwrap();
@@ -175,7 +185,7 @@ macro_rules! eval_fails {
}
_ => continue,
};
- if async_std::task::block_on(pipeline.eval(&$env)).is_err() {
+ if pipeline.eval(&$env).await.is_err() {
fail = true;
}
}
@@ -206,7 +216,7 @@ fn test_basic() {
);
// XXX this parse may change in the future
- let exe = std::env::current_exe()
+ let exe = crate::info::current_exe()
.unwrap()
.into_os_string()
.into_string()
@@ -223,6 +233,9 @@ fn test_basic() {
)
))
);
+
+ parse_eq!("foo ''", cs!(p!((0, 6), e!(w!("foo"), w!()))));
+ parse_eq!("foo \"\"", cs!(p!((0, 6), e!(w!("foo"), w!()))));
}
#[test]
@@ -426,8 +439,9 @@ fn test_alternation() {
);
}
+#[tokio::main]
#[test]
-fn test_eval_alternation() {
+async fn test_eval_alternation() {
let mut env = Env::new().unwrap();
env.set_var("HOME", "/home/test");
env.set_var("foo", "value-of-foo");
@@ -464,8 +478,9 @@ fn test_eval_alternation() {
);
}
+#[tokio::main]
#[test]
-fn test_eval_glob() {
+async fn test_eval_glob() {
let env = Env::new().unwrap();
eval_eq!(
@@ -484,3 +499,9 @@ fn test_eval_glob() {
eval_fails!("echo *.doesnotexist", env);
eval_fails!("echo *.{toml,doesnotexist}", env);
}
+
+#[test]
+fn test_deserialize() {
+ deserialize_eq!("foo", e!(w!("foo")));
+ deserialize_eq!("foo bar baz", e!(w!("foo"), w!("bar"), w!("baz")));
+}
diff --git a/src/prelude.rs b/src/prelude.rs
index 6789a1f..bc48955 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -1,11 +1,51 @@
pub use crate::env::Env;
+pub use anyhow::{anyhow, Result};
-pub use async_std::io::{ReadExt as _, WriteExt as _};
-pub use async_std::stream::StreamExt as _;
-pub use futures_lite::future::FutureExt as _;
+pub use std::io::{Read as _, Write as _};
+
+pub use futures_util::future::FutureExt as _;
+pub use futures_util::stream::StreamExt as _;
+pub use futures_util::stream::TryStreamExt as _;
+pub use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
-pub use async_std::os::unix::process::CommandExt as _;
pub use std::os::unix::ffi::{OsStrExt as _, OsStringExt as _};
pub use std::os::unix::io::{AsRawFd as _, FromRawFd as _, IntoRawFd as _};
pub use std::os::unix::process::ExitStatusExt as _;
pub use users::os::unix::UserExt as _;
+
+pub use ext::Result as _;
+
+mod ext {
+ pub trait Result {
+ type T;
+ type E;
+
+ fn allow(self, allow_e: Self::E) -> Self;
+ fn allow_with(self, allow_e: Self::E, default_t: Self::T) -> Self;
+ }
+
+ impl<T, E> Result for std::result::Result<T, E>
+ where
+ T: std::default::Default,
+ E: std::cmp::PartialEq,
+ {
+ type T = T;
+ type E = E;
+
+ fn allow(self, allow_e: Self::E) -> Self {
+ self.or_else(|e| {
+ if e == allow_e {
+ Ok(std::default::Default::default())
+ } else {
+ Err(e)
+ }
+ })
+ }
+
+ fn allow_with(self, allow_e: Self::E, default_t: Self::T) -> Self {
+ self.or_else(
+ |e| if e == allow_e { Ok(default_t) } else { Err(e) },
+ )
+ }
+ }
+}
diff --git a/src/runner/builtins/command.rs b/src/runner/builtins/command.rs
index e3a3fce..16d8b40 100644
--- a/src/runner/builtins/command.rs
+++ b/src/runner/builtins/command.rs
@@ -38,8 +38,8 @@ impl Command {
self.cfg.io.set_stderr(fh);
}
- // Safety: see pre_exec in async_std::os::unix::process::CommandExt (this
- // is just a wrapper)
+ // Safety: see pre_exec in tokio::process::Command (this is just a
+ // wrapper)
pub unsafe fn pre_exec<F>(&mut self, f: F)
where
F: 'static + FnMut() -> std::io::Result<()> + Send + Sync,
@@ -51,7 +51,7 @@ impl Command {
self.cfg.io.apply_redirects(redirects);
}
- pub fn spawn(self, env: &Env) -> anyhow::Result<Child> {
+ pub fn spawn(self, env: &Env) -> Result<Child> {
let Self { f, exe, cfg } = self;
(f)(exe, env, cfg)
}
@@ -73,8 +73,8 @@ impl Cfg {
&self.io
}
- // Safety: see pre_exec in async_std::os::unix::process::CommandExt (this
- // is just a wrapper)
+ // Safety: see pre_exec in tokio::process::Command (this is just a
+ // wrapper)
pub unsafe fn pre_exec<F>(&mut self, f: F)
where
F: 'static + FnMut() -> std::io::Result<()> + Send + Sync,
@@ -187,10 +187,10 @@ impl Io {
}
}
- pub async fn read_line_stdin(&self) -> anyhow::Result<(String, bool)> {
- let mut buf = vec![];
- if let Some(fh) = self.stdin() {
- if let File::In(fh) = &*fh {
+ pub fn read_line_stdin(&self) -> Result<(String, bool)> {
+ let mut line = vec![];
+ if let Some(file) = self.stdin() {
+ if let File::In(fh) = &*file {
// we have to read only a single character at a time here
// because stdin needs to be shared across all commands in the
// command list, some of which may be builtins and others of
@@ -199,36 +199,27 @@ impl Io {
// no longer be available to the next command, since we have
// them buffered in memory rather than them being on the stdin
// pipe.
- let mut c = [0_u8];
- loop {
- match (&*fh).read_exact(&mut c[..]).await {
- Ok(()) => {}
- Err(e) => {
- if e.kind() == std::io::ErrorKind::UnexpectedEof {
- break;
- }
- return Err(e.into());
- }
- }
- if c[0] == b'\n' {
+ for byte in fh.bytes() {
+ let byte = byte?;
+ line.push(byte);
+ if byte == b'\n' {
break;
}
- buf.push(c[0]);
}
}
}
- let done = buf.is_empty();
- let mut buf = String::from_utf8(buf).unwrap();
- if buf.ends_with('\n') {
- buf.truncate(buf.len() - 1);
+ let done = line.is_empty();
+ let mut line = String::from_utf8(line).unwrap();
+ if line.ends_with('\n') {
+ line.truncate(line.len() - 1);
}
- Ok((buf, done))
+ Ok((line, done))
}
- pub async fn write_stdout(&self, buf: &[u8]) -> anyhow::Result<()> {
- if let Some(fh) = self.stdout() {
- if let File::Out(fh) = &*fh {
- Ok((&*fh).write_all(buf).await.map(|_| ())?)
+ pub fn write_stdout(&self, buf: &[u8]) -> Result<()> {
+ if let Some(file) = self.stdout() {
+ if let File::Out(fh) = &*file {
+ Ok((&*fh).write_all(buf)?)
} else {
Ok(())
}
@@ -237,10 +228,10 @@ impl Io {
}
}
- pub async fn write_stderr(&self, buf: &[u8]) -> anyhow::Result<()> {
- if let Some(fh) = self.stderr() {
- if let File::Out(fh) = &*fh {
- Ok((&*fh).write_all(buf).await.map(|_| ())?)
+ pub fn write_stderr(&self, buf: &[u8]) -> Result<()> {
+ if let Some(file) = self.stderr() {
+ if let File::Out(fh) = &*file {
+ Ok((&*fh).write_all(buf)?)
} else {
Ok(())
}
@@ -299,19 +290,19 @@ impl Drop for Io {
#[derive(Debug)]
pub enum File {
- In(async_std::fs::File),
- Out(async_std::fs::File),
+ In(std::fs::File),
+ Out(std::fs::File),
}
impl File {
// Safety: fd must not be owned by any other File object
pub unsafe fn input(fd: std::os::unix::io::RawFd) -> Self {
- Self::In(async_std::fs::File::from_raw_fd(fd))
+ Self::In(std::fs::File::from_raw_fd(fd))
}
// Safety: fd must not be owned by any other File object
pub unsafe fn output(fd: std::os::unix::io::RawFd) -> Self {
- Self::Out(async_std::fs::File::from_raw_fd(fd))
+ Self::Out(std::fs::File::from_raw_fd(fd))
}
fn maybe_drop(file: std::sync::Arc<Self>) {
@@ -339,59 +330,43 @@ impl std::os::unix::io::IntoRawFd for File {
}
}
-pub struct Child<'a> {
- fut: std::pin::Pin<
- Box<
- dyn std::future::Future<Output = std::process::ExitStatus>
- + Sync
- + Send
- + 'a,
- >,
- >,
- wrapped_child: Option<Box<crate::runner::Child<'a>>>,
+pub enum Child {
+ Task(tokio::task::JoinHandle<std::process::ExitStatus>),
+ Wrapped(Box<crate::runner::Child>),
}
-impl<'a> Child<'a> {
- pub fn new_fut<F>(fut: F) -> Self
+impl Child {
+ pub fn new_task<F>(f: F) -> Self
where
- F: std::future::Future<Output = std::process::ExitStatus>
- + Sync
- + Send
- + 'a,
+ F: FnOnce() -> std::process::ExitStatus + Send + 'static,
{
- Self {
- fut: Box::pin(fut),
- wrapped_child: None,
- }
+ Self::Task(tokio::task::spawn_blocking(f))
}
- pub fn new_wrapped(child: crate::runner::Child<'a>) -> Self {
- Self {
- fut: Box::pin(async move { unreachable!() }),
- wrapped_child: Some(Box::new(child)),
- }
+ pub fn new_wrapped(child: crate::runner::Child) -> Self {
+ Self::Wrapped(Box::new(child))
}
pub fn id(&self) -> Option<u32> {
- self.wrapped_child.as_ref().and_then(|cmd| cmd.id())
+ match self {
+ Self::Task(_) => None,
+ Self::Wrapped(child) => child.id(),
+ }
}
pub fn status(
self,
) -> std::pin::Pin<
Box<
- dyn std::future::Future<
- Output = anyhow::Result<async_std::process::ExitStatus>,
- > + Send
- + Sync
- + 'a,
+ dyn std::future::Future<Output = Result<std::process::ExitStatus>>
+ + Send
+ + Sync,
>,
> {
Box::pin(async move {
- if let Some(child) = self.wrapped_child {
- child.status().await
- } else {
- Ok(self.fut.await)
+ match self {
+ Self::Task(task) => task.await.map_err(|e| anyhow!(e)),
+ Self::Wrapped(child) => child.status().await,
}
})
}
diff --git a/src/runner/builtins/mod.rs b/src/runner/builtins/mod.rs
index 5205856..b714c58 100644
--- a/src/runner/builtins/mod.rs
+++ b/src/runner/builtins/mod.rs
@@ -7,7 +7,7 @@ type Builtin = &'static (dyn for<'a> Fn(
crate::parse::Exe,
&'a Env,
command::Cfg,
-) -> anyhow::Result<command::Child<'a>>
+) -> Result<command::Child>
+ Sync
+ Send);
@@ -33,7 +33,6 @@ macro_rules! bail {
$cfg.io().write_stderr(
format!("{}: {}\n", $exe.exe().display(), $msg).as_bytes()
)
- .await
.unwrap();
return std::process::ExitStatus::from_raw(1 << 8);
};
@@ -41,12 +40,10 @@ macro_rules! bail {
$cfg.io().write_stderr(
format!("{}: ", $exe.exe().display()).as_bytes()
)
- .await
.unwrap();
$cfg.io().write_stderr(format!($msg, $($arg)*).as_bytes())
- .await
.unwrap();
- $cfg.io().write_stderr(b"\n").await.unwrap();
+ $cfg.io().write_stderr(b"\n").unwrap();
return std::process::ExitStatus::from_raw(1 << 8);
};
}
@@ -57,22 +54,20 @@ fn cd(
exe: crate::parse::Exe,
env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
- async fn async_cd(
- exe: crate::parse::Exe,
- env: &Env,
- cfg: command::Cfg,
- ) -> std::process::ExitStatus {
+) -> Result<command::Child> {
+ let prev_pwd = env.prev_pwd();
+ let home = env.var("HOME");
+ Ok(command::Child::new_task(move || {
let dir = if let Some(dir) = exe.args().get(0) {
if dir.is_empty() {
".".to_string().into()
} else if dir == "-" {
- env.prev_pwd()
+ prev_pwd
} else {
dir.into()
}
} else {
- let dir = env.var("HOME");
+ let dir = home;
if let Some(dir) = dir {
dir.into()
} else {
@@ -88,25 +83,17 @@ fn cd(
dir.display()
);
}
- async_std::process::ExitStatus::from_raw(0)
- }
-
- Ok(command::Child::new_fut(async move {
- async_cd(exe, env, cfg).await
+ std::process::ExitStatus::from_raw(0)
}))
}
#[allow(clippy::unnecessary_wraps)]
fn set(
exe: crate::parse::Exe,
- env: &Env,
+ _env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
- async fn async_set(
- exe: crate::parse::Exe,
- _env: &Env,
- cfg: command::Cfg,
- ) -> std::process::ExitStatus {
+) -> Result<command::Child> {
+ Ok(command::Child::new_task(move || {
let k = if let Some(k) = exe.args().get(0).map(String::as_str) {
k
} else {
@@ -119,25 +106,17 @@ fn set(
};
std::env::set_var(k, v);
- async_std::process::ExitStatus::from_raw(0)
- }
-
- Ok(command::Child::new_fut(async move {
- async_set(exe, env, cfg).await
+ std::process::ExitStatus::from_raw(0)
}))
}
#[allow(clippy::unnecessary_wraps)]
fn unset(
exe: crate::parse::Exe,
- env: &Env,
+ _env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
- async fn async_unset(
- exe: crate::parse::Exe,
- _env: &Env,
- cfg: command::Cfg,
- ) -> std::process::ExitStatus {
+) -> Result<command::Child> {
+ Ok(command::Child::new_task(move || {
let k = if let Some(k) = exe.args().get(0).map(String::as_str) {
k
} else {
@@ -145,11 +124,7 @@ fn unset(
};
std::env::remove_var(k);
- async_std::process::ExitStatus::from_raw(0)
- }
-
- Ok(command::Child::new_fut(async move {
- async_unset(exe, env, cfg).await
+ std::process::ExitStatus::from_raw(0)
}))
}
@@ -159,22 +134,17 @@ fn unset(
// this later, since the binary seems totally fine
fn echo(
exe: crate::parse::Exe,
- env: &Env,
+ _env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
- async fn async_echo(
- exe: crate::parse::Exe,
- _env: &Env,
- cfg: command::Cfg,
- ) -> std::process::ExitStatus {
+) -> Result<command::Child> {
+ Ok(command::Child::new_task(move || {
macro_rules! write_stdout {
($bytes:expr) => {
- if let Err(e) = cfg.io().write_stdout($bytes).await {
+ if let Err(e) = cfg.io().write_stdout($bytes) {
cfg.io()
.write_stderr(format!("echo: {}", e).as_bytes())
- .await
.unwrap();
- return async_std::process::ExitStatus::from_raw(1 << 8);
+ return std::process::ExitStatus::from_raw(1 << 8);
}
};
}
@@ -188,32 +158,24 @@ fn echo(
}
}
- async_std::process::ExitStatus::from_raw(0)
- }
-
- Ok(command::Child::new_fut(async move {
- async_echo(exe, env, cfg).await
+ std::process::ExitStatus::from_raw(0)
}))
}
#[allow(clippy::unnecessary_wraps)]
fn read(
exe: crate::parse::Exe,
- env: &Env,
+ _env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
- async fn async_read(
- exe: crate::parse::Exe,
- _env: &Env,
- cfg: command::Cfg,
- ) -> std::process::ExitStatus {
+) -> Result<command::Child> {
+ Ok(command::Child::new_task(move || {
let var = if let Some(var) = exe.args().get(0).map(String::as_str) {
var
} else {
bail!(cfg, exe, "usage: read var");
};
- let (val, done) = match cfg.io().read_line_stdin().await {
+ let (val, done) = match cfg.io().read_line_stdin() {
Ok((line, done)) => (line, done),
Err(e) => {
bail!(cfg, exe, e);
@@ -221,15 +183,7 @@ fn read(
};
std::env::set_var(var, val);
- async_std::process::ExitStatus::from_raw(if done {
- 1 << 8
- } else {
- 0
- })
- }
-
- Ok(command::Child::new_fut(async move {
- async_read(exe, env, cfg).await
+ std::process::ExitStatus::from_raw(if done { 1 << 8 } else { 0 })
}))
}
@@ -237,7 +191,7 @@ fn and(
mut exe: crate::parse::Exe,
env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
+) -> Result<command::Child> {
exe.shift();
if env.latest_status().success() {
let mut cmd = crate::runner::Command::new(exe, cfg.io().clone());
@@ -245,7 +199,7 @@ fn and(
Ok(command::Child::new_wrapped(cmd.spawn(env)?))
} else {
let status = env.latest_status();
- Ok(command::Child::new_fut(async move { status }))
+ Ok(command::Child::new_task(move || status))
}
}
@@ -253,11 +207,11 @@ fn or(
mut exe: crate::parse::Exe,
env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
+) -> Result<command::Child> {
exe.shift();
if env.latest_status().success() {
let status = env.latest_status();
- Ok(command::Child::new_fut(async move { status }))
+ Ok(command::Child::new_task(move || status))
} else {
let mut cmd = crate::runner::Command::new(exe, cfg.io().clone());
cfg.setup_command(&mut cmd);
@@ -269,9 +223,9 @@ fn command(
mut exe: crate::parse::Exe,
env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
+) -> Result<command::Child> {
exe.shift();
- let mut cmd = crate::runner::Command::new_binary(exe);
+ let mut cmd = crate::runner::Command::new_binary(&exe);
cfg.setup_command(&mut cmd);
Ok(command::Child::new_wrapped(cmd.spawn(env)?))
}
@@ -280,7 +234,7 @@ fn builtin(
mut exe: crate::parse::Exe,
env: &Env,
cfg: command::Cfg,
-) -> anyhow::Result<command::Child> {
+) -> Result<command::Child> {
exe.shift();
let mut cmd = crate::runner::Command::new_builtin(exe, cfg.io().clone());
cfg.setup_command(&mut cmd);
diff --git a/src/runner/command.rs b/src/runner/command.rs
index 5d4c11e..cbc8dee 100644
--- a/src/runner/command.rs
+++ b/src/runner/command.rs
@@ -8,13 +8,14 @@ pub struct Command {
Box<dyn FnMut() -> std::io::Result<()> + Send + Sync + 'static>,
>,
}
+
impl Command {
pub fn new(exe: crate::parse::Exe, io: super::builtins::Io) -> Self {
let exe_path = exe.exe().to_path_buf();
let redirects = exe.redirects().to_vec();
Self {
inner: super::builtins::Command::new(exe, io).map_or_else(
- |exe| Self::new_binary(exe).inner,
+ |exe| Self::new_binary(&exe).inner,
Inner::Builtin,
),
exe: exe_path,
@@ -23,11 +24,10 @@ impl Command {
}
}
- #[allow(clippy::needless_pass_by_value)]
- pub fn new_binary(exe: crate::parse::Exe) -> Self {
+ pub fn new_binary(exe: &crate::parse::Exe) -> Self {
let exe_path = exe.exe().to_path_buf();
let redirects = exe.redirects().to_vec();
- let mut cmd = async_std::process::Command::new(exe.exe());
+ let mut cmd = tokio::process::Command::new(exe.exe());
cmd.args(exe.args());
Self {
inner: Inner::Binary(cmd),
@@ -85,8 +85,8 @@ impl Command {
}
}
- // Safety: see pre_exec in async_std::os::unix::process::CommandExt (this
- // is just a wrapper)
+ // Safety: see pre_exec in tokio::process::Command (this is just a
+ // wrapper)
pub unsafe fn pre_exec<F>(&mut self, f: F)
where
F: 'static + FnMut() -> std::io::Result<()> + Send + Sync,
@@ -94,7 +94,7 @@ impl Command {
self.pre_exec = Some(Box::new(f));
}
- pub fn spawn(self, env: &Env) -> anyhow::Result<Child> {
+ pub fn spawn(self, env: &Env) -> Result<Child> {
let Self {
inner,
exe,
@@ -127,7 +127,7 @@ impl Command {
// functions
unsafe { cmd.pre_exec(pre_exec) };
Ok(Child::Binary(cmd.spawn().map_err(|e| {
- anyhow::anyhow!(
+ anyhow!(
"{}: {}",
crate::format::io_error(&e),
exe.display()
@@ -146,19 +146,19 @@ impl Command {
}
pub enum Inner {
- Binary(async_std::process::Command),
+ Binary(tokio::process::Command),
Builtin(super::builtins::Command),
}
-pub enum Child<'a> {
- Binary(async_std::process::Child),
- Builtin(super::builtins::Child<'a>),
+pub enum Child {
+ Binary(tokio::process::Child),
+ Builtin(super::builtins::Child),
}
-impl<'a> Child<'a> {
+impl Child {
pub fn id(&self) -> Option<u32> {
match self {
- Self::Binary(child) => Some(child.id()),
+ Self::Binary(child) => child.id(),
Self::Builtin(child) => child.id(),
}
}
@@ -167,16 +167,15 @@ impl<'a> Child<'a> {
self,
) -> std::pin::Pin<
Box<
- dyn std::future::Future<
- Output = anyhow::Result<std::process::ExitStatus>,
- > + Send
- + Sync
- + 'a,
+ dyn std::future::Future<Output = Result<std::process::ExitStatus>>
+ + Send
+ + Sync,
>,
> {
Box::pin(async move {
match self {
- Self::Binary(child) => Ok(child.status_no_drop().await?),
+ // this case is handled by waitpid
+ Self::Binary(_) => unreachable!(),
Self::Builtin(child) => Ok(child.status().await?),
}
})
diff --git a/src/runner/mod.rs b/src/runner/mod.rs
index 01a87b9..91e268a 100644
--- a/src/runner/mod.rs
+++ b/src/runner/mod.rs
@@ -4,13 +4,12 @@ mod builtins;
mod command;
pub use command::{Child, Command};
mod prelude;
-
-const PID0: nix::unistd::Pid = nix::unistd::Pid::from_raw(0);
+mod sys;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Event {
- RunPipeline(usize, (usize, usize)),
- Suspend(usize),
+ RunPipeline((usize, usize)),
+ Suspend,
Exit(Env),
}
@@ -69,12 +68,13 @@ enum Frame {
For(bool, usize, Vec<String>),
}
-pub async fn run(
- commands: &str,
- shell_write: Option<&async_std::fs::File>,
-) -> anyhow::Result<i32> {
+pub async fn main(
+ commands: String,
+ shell_write: &mut Option<tokio::fs::File>,
+) -> Result<i32> {
let mut env = Env::new_from_env()?;
- run_commands(commands, &mut env, shell_write).await?;
+ let config = crate::config::Config::load()?;
+ run_commands(commands, &mut env, &config, shell_write).await?;
let status = env.latest_status();
write_event(shell_write, Event::Exit(env)).await?;
@@ -85,11 +85,12 @@ pub async fn run(
}
async fn run_commands(
- commands: &str,
+ commands: String,
env: &mut Env,
- shell_write: Option<&async_std::fs::File>,
-) -> anyhow::Result<()> {
- let commands = crate::parse::ast::Commands::parse(commands)?;
+ config: &crate::config::Config,
+ shell_write: &mut Option<tokio::fs::File>,
+) -> Result<()> {
+ let commands = crate::parse::ast::Commands::parse(&commands)?;
let commands = commands.commands();
let mut pc = 0;
let mut stack = Stack::new();
@@ -97,7 +98,8 @@ async fn run_commands(
match &commands[pc] {
crate::parse::ast::Command::Pipeline(pipeline) => {
if stack.should_execute() {
- run_pipeline(pipeline.clone(), env, shell_write).await?;
+ run_pipeline(pipeline.clone(), env, config, shell_write)
+ .await?;
}
pc += 1;
}
@@ -108,7 +110,8 @@ async fn run_commands(
}
if should {
let status = env.latest_status();
- run_pipeline(pipeline.clone(), env, shell_write).await?;
+ run_pipeline(pipeline.clone(), env, config, shell_write)
+ .await?;
if let Some(Frame::If(should, found)) = stack.top_mut() {
*should = env.latest_status().success();
if *should {
@@ -128,7 +131,8 @@ async fn run_commands(
}
if should {
let status = env.latest_status();
- run_pipeline(pipeline.clone(), env, shell_write).await?;
+ run_pipeline(pipeline.clone(), env, config, shell_write)
+ .await?;
if let Some(Frame::While(should, _)) = stack.top_mut() {
*should = env.latest_status().success();
} else {
@@ -153,7 +157,7 @@ async fn run_commands(
.map(IntoIterator::into_iter)
})
.collect::<futures_util::stream::FuturesOrdered<_>>()
- .collect::<Result<Vec<_>, _>>().await?
+ .try_collect::<Vec<_>>().await?
.into_iter()
.flatten()
.collect()
@@ -188,8 +192,13 @@ async fn run_commands(
*should = false;
} else if let Some(pipeline) = pipeline {
let status = env.latest_status();
- run_pipeline(pipeline.clone(), env, shell_write)
- .await?;
+ run_pipeline(
+ pipeline.clone(),
+ env,
+ config,
+ shell_write,
+ )
+ .await?;
*should = env.latest_status().success();
if *should {
*found = true;
@@ -232,30 +241,54 @@ async fn run_commands(
async fn run_pipeline(
pipeline: crate::parse::ast::Pipeline,
env: &mut Env,
- shell_write: Option<&async_std::fs::File>,
-) -> anyhow::Result<()> {
- write_event(shell_write, Event::RunPipeline(env.idx(), pipeline.span()))
- .await?;
+ config: &crate::config::Config,
+ shell_write: &mut Option<tokio::fs::File>,
+) -> Result<()> {
+ write_event(shell_write, Event::RunPipeline(pipeline.span())).await?;
// Safety: pipelines are run serially, so only one copy of these will ever
// exist at once. note that reusing a single copy of these at the top
// level would not be safe, because in the case of a command line like
// "echo foo; ls", we would pass the stdout fd to the ls process while it
// is still open here, and may still have data buffered.
- let stdin = unsafe { async_std::fs::File::from_raw_fd(0) };
- let stdout = unsafe { async_std::fs::File::from_raw_fd(1) };
- let stderr = unsafe { async_std::fs::File::from_raw_fd(2) };
+ let stdin = unsafe { std::fs::File::from_raw_fd(0) };
+ let stdout = unsafe { std::fs::File::from_raw_fd(1) };
+ let stderr = unsafe { std::fs::File::from_raw_fd(2) };
let mut io = builtins::Io::new();
io.set_stdin(stdin);
io.set_stdout(stdout);
io.set_stderr(stderr);
let pwd = env.pwd().to_path_buf();
- let pipeline = pipeline.eval(env).await?;
let interactive = shell_write.is_some();
- let (children, pg) = spawn_children(pipeline, env, &io, interactive)?;
- let status = wait_children(children, pg, env, &io, shell_write).await;
+ let pipeline = pipeline.eval(env).await?;
+ let mut exes: Vec<_> = pipeline.into_exes().collect();
+ for exe in &mut exes {
+ let mut seen = std::collections::HashSet::new();
+ while let Some(alias) = config.alias_for(exe.exe()) {
+ let mut new = alias.clone().eval(env).await?;
+ let override_self = exe.exe() == new.exe();
+ if seen.contains(new.exe()) {
+ return Err(anyhow!(
+ "recursive alias found: {}",
+ new.exe().display()
+ ));
+ }
+ seen.insert(new.exe().to_path_buf());
+ new.append(exe.clone());
+ *exe = new;
+ if override_self {
+ break;
+ }
+ }
+ }
+ let cmds = exes
+ .into_iter()
+ .map(|exe| Command::new(exe, io.clone()))
+ .collect();
+ let (children, pg) = spawn_children(cmds, env, interactive)?;
+ let status = wait_children(children, pg, shell_write).await;
if interactive {
- set_foreground_pg(nix::unistd::getpid())?;
+ sys::set_foreground_pg(nix::unistd::getpid())?;
}
env.update()?;
env.set_status(status);
@@ -266,28 +299,23 @@ async fn run_pipeline(
}
async fn write_event(
- fh: Option<&async_std::fs::File>,
+ fh: &mut Option<tokio::fs::File>,
event: Event,
-) -> anyhow::Result<()> {
- if let Some(mut fh) = fh {
+) -> Result<()> {
+ if let Some(fh) = fh {
fh.write_all(&bincode::serialize(&event)?).await?;
fh.flush().await?;
}
Ok(())
}
-fn spawn_children<'a>(
- pipeline: crate::parse::Pipeline,
- env: &'a Env,
- io: &builtins::Io,
+fn spawn_children(
+ mut cmds: Vec<Command>,
+ env: &Env,
interactive: bool,
-) -> anyhow::Result<(Vec<Child<'a>>, Option<nix::unistd::Pid>)> {
- let mut cmds: Vec<_> = pipeline
- .into_exes()
- .map(|exe| Command::new(exe, io.clone()))
- .collect();
+) -> Result<(Vec<Child>, Option<nix::unistd::Pid>)> {
for i in 0..(cmds.len() - 1) {
- let (r, w) = pipe()?;
+ let (r, w) = sys::pipe()?;
cmds[i].stdout(w);
cmds[i + 1].stdin(r);
}
@@ -298,18 +326,18 @@ fn spawn_children<'a>(
// Safety: setpgid is an async-signal-safe function
unsafe {
cmd.pre_exec(move || {
- setpgid_child(pg_pid)?;
+ sys::setpgid_child(pg_pid)?;
Ok(())
});
}
let child = cmd.spawn(env)?;
if let Some(id) = child.id() {
- let child_pid = id_to_pid(id);
- setpgid_parent(child_pid, pg_pid)?;
+ let child_pid = sys::id_to_pid(id);
+ sys::setpgid_parent(child_pid, pg_pid)?;
if pg_pid.is_none() {
pg_pid = Some(child_pid);
if interactive {
- set_foreground_pg(child_pid)?;
+ sys::set_foreground_pg(child_pid)?;
}
}
}
@@ -319,24 +347,18 @@ fn spawn_children<'a>(
}
async fn wait_children(
- children: Vec<Child<'_>>,
+ children: Vec<Child>,
pg: Option<nix::unistd::Pid>,
- env: &Env,
- io: &builtins::Io,
- shell_write: Option<&async_std::fs::File>,
+ shell_write: &mut Option<tokio::fs::File>,
) -> std::process::ExitStatus {
enum Res {
Child(nix::Result<nix::sys::wait::WaitStatus>),
- Builtin(Option<(anyhow::Result<std::process::ExitStatus>, bool)>),
+ Builtin((Result<std::process::ExitStatus>, bool)),
}
macro_rules! bail {
($e:expr) => {
- // if writing to stderr is not possible, we still want to exit
- // normally with a failure exit code
- #[allow(clippy::let_underscore_drop)]
- let _ =
- io.write_stderr(format!("nbsh: {}\n", $e).as_bytes()).await;
+ eprintln!("nbsh: {}\n", $e);
return std::process::ExitStatus::from_raw(1 << 8);
};
}
@@ -351,10 +373,11 @@ async fn wait_children(
let mut children: std::collections::HashMap<_, _> = children
.into_iter()
.map(|(i, child)| {
- (id_to_pid(child.id().unwrap()), (child, i == count - 1))
+ (sys::id_to_pid(child.id().unwrap()), (child, i == count - 1))
})
.collect();
- let mut builtins: futures_util::stream::FuturesUnordered<_> =
+ let mut builtin_count = builtins.len();
+ let builtins: futures_util::stream::FuturesUnordered<_> =
builtins
.into_iter()
.map(|(i, child)| async move {
@@ -362,47 +385,40 @@ async fn wait_children(
})
.collect();
- let (wait_w, wait_r) = async_std::channel::unbounded();
- let new_wait = move || {
- if let Some(pg) = pg {
- let wait_w = wait_w.clone();
- async_std::task::spawn(async move {
- let res = blocking::unblock(move || {
- nix::sys::wait::waitpid(
- neg_pid(pg),
- Some(nix::sys::wait::WaitPidFlag::WUNTRACED),
- )
- })
- .await;
- if wait_w.is_closed() {
- // we shouldn't be able to drop real process terminations
+ let (wait_w, wait_r) = tokio::sync::mpsc::unbounded_channel();
+ if let Some(pg) = pg {
+ tokio::task::spawn_blocking(move || loop {
+ let res = nix::sys::wait::waitpid(
+ sys::neg_pid(pg),
+ Some(nix::sys::wait::WaitPidFlag::WUNTRACED),
+ );
+ match wait_w.send(res) {
+ Ok(_) => {}
+ Err(tokio::sync::mpsc::error::SendError(res)) => {
+ // we should never drop wait_r while there are still valid
+ // things to read
assert!(res.is_err());
- } else {
- wait_w.send(res).await.unwrap();
+ break;
}
- });
- }
- };
-
- new_wait();
- loop {
- if children.is_empty() && builtins.is_empty() {
- break;
- }
+ }
+ });
+ }
- let child = async { Res::Child(wait_r.recv().await.unwrap()) };
- let builtin = async {
- Res::Builtin(if builtins.is_empty() {
- std::future::pending().await
- } else {
- builtins.next().await
- })
- };
- match child.race(builtin).await {
+ let mut stream: futures_util::stream::SelectAll<_> = [
+ tokio_stream::wrappers::UnboundedReceiverStream::new(wait_r)
+ .map(Res::Child)
+ .boxed(),
+ builtins.map(Res::Builtin).boxed(),
+ ]
+ .into_iter()
+ .collect();
+ while let Some(res) = stream.next().await {
+ match res {
Res::Child(Ok(status)) => {
match status {
- // we can't call child.status() here to unify these branches
- // because our waitpid call already collected the status
+ // we can't call child.status() here to unify these
+ // branches because our waitpid call already collected the
+ // status
nix::sys::wait::WaitStatus::Exited(pid, code) => {
let (_, last) = children.remove(&pid).unwrap();
if last {
@@ -432,11 +448,8 @@ async fn wait_children(
}
nix::sys::wait::WaitStatus::Stopped(pid, signal) => {
if signal == nix::sys::signal::Signal::SIGTSTP {
- if let Err(e) = write_event(
- shell_write,
- Event::Suspend(env.idx()),
- )
- .await
+ if let Err(e) =
+ write_event(shell_write, Event::Suspend).await
{
bail!(e);
}
@@ -450,12 +463,11 @@ async fn wait_children(
}
_ => {}
}
- new_wait();
}
Res::Child(Err(e)) => {
bail!(e);
}
- Res::Builtin(Some((Ok(status), last))) => {
+ Res::Builtin((Ok(status), last)) => {
// this conversion is safe because the Signal enum is
// repr(i32)
#[allow(clippy::as_conversions)]
@@ -471,99 +483,17 @@ async fn wait_children(
if last {
final_status = Some(status);
}
+ builtin_count -= 1;
}
- Res::Builtin(Some((Err(e), _))) => {
+ Res::Builtin((Err(e), _)) => {
bail!(e);
}
- Res::Builtin(None) => {}
}
- }
- final_status.unwrap()
-}
-
-fn pipe() -> anyhow::Result<(std::fs::File, std::fs::File)> {
- let (r, w) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
- // Safety: these file descriptors were just returned by pipe2 above, and
- // are only available in this function, so nothing else can be accessing
- // them
- Ok((unsafe { std::fs::File::from_raw_fd(r) }, unsafe {
- std::fs::File::from_raw_fd(w)
- }))
-}
-
-fn set_foreground_pg(pg: nix::unistd::Pid) -> anyhow::Result<()> {
- let pty = nix::fcntl::open(
- "/dev/tty",
- nix::fcntl::OFlag::empty(),
- nix::sys::stat::Mode::empty(),
- )?;
-
- // if a background process calls tcsetpgrp, the kernel will send it
- // SIGTTOU which suspends it. if that background process is the session
- // leader and doesn't have SIGTTOU blocked, the kernel will instead just
- // return ENOTTY from the tcsetpgrp call rather than sending a signal to
- // avoid deadlocking the process. therefore, we need to ensure that
- // SIGTTOU is blocked here.
-
- // Safety: setting a signal handler to SigIgn is always safe
- unsafe {
- nix::sys::signal::signal(
- nix::sys::signal::Signal::SIGTTOU,
- nix::sys::signal::SigHandler::SigIgn,
- )?;
- }
- let res = nix::unistd::tcsetpgrp(pty, pg);
- // Safety: setting a signal handler to SigDfl is always safe
- unsafe {
- nix::sys::signal::signal(
- nix::sys::signal::Signal::SIGTTOU,
- nix::sys::signal::SigHandler::SigDfl,
- )?;
- }
- res?;
-
- nix::unistd::close(pty)?;
-
- nix::sys::signal::kill(neg_pid(pg), nix::sys::signal::Signal::SIGCONT)
- .or_else(|e| {
- // the process group has already exited
- if e == nix::errno::Errno::ESRCH {
- Ok(())
- } else {
- Err(e)
- }
- })?;
-
- Ok(())
-}
-
-fn setpgid_child(pg: Option<nix::unistd::Pid>) -> std::io::Result<()> {
- nix::unistd::setpgid(PID0, pg.unwrap_or(PID0))?;
- Ok(())
-}
-
-fn setpgid_parent(
- pid: nix::unistd::Pid,
- pg: Option<nix::unistd::Pid>,
-) -> anyhow::Result<()> {
- nix::unistd::setpgid(pid, pg.unwrap_or(PID0)).or_else(|e| {
- // EACCES means that the child already called exec, but if it did,
- // then it also must have already called setpgid itself, so we don't
- // care. ESRCH means that the process already exited, which is similar
- if e == nix::errno::Errno::EACCES || e == nix::errno::Errno::ESRCH {
- Ok(())
- } else {
- Err(e)
+ if children.is_empty() && builtin_count == 0 {
+ break;
}
- })?;
- Ok(())
-}
-
-fn id_to_pid(id: u32) -> nix::unistd::Pid {
- nix::unistd::Pid::from_raw(id.try_into().unwrap())
-}
+ }
-fn neg_pid(pid: nix::unistd::Pid) -> nix::unistd::Pid {
- nix::unistd::Pid::from_raw(-pid.as_raw())
+ final_status.unwrap()
}
diff --git a/src/runner/sys.rs b/src/runner/sys.rs
new file mode 100644
index 0000000..b6a9428
--- /dev/null
+++ b/src/runner/sys.rs
@@ -0,0 +1,79 @@
+use crate::runner::prelude::*;
+
+const PID0: nix::unistd::Pid = nix::unistd::Pid::from_raw(0);
+
+pub fn pipe() -> Result<(std::fs::File, std::fs::File)> {
+ let (r, w) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
+ // Safety: these file descriptors were just returned by pipe2 above, and
+ // are only available in this function, so nothing else can be accessing
+ // them
+ Ok((unsafe { std::fs::File::from_raw_fd(r) }, unsafe {
+ std::fs::File::from_raw_fd(w)
+ }))
+}
+
+pub fn set_foreground_pg(pg: nix::unistd::Pid) -> Result<()> {
+ let pty = nix::fcntl::open(
+ "/dev/tty",
+ nix::fcntl::OFlag::empty(),
+ nix::sys::stat::Mode::empty(),
+ )?;
+
+ // if a background process calls tcsetpgrp, the kernel will send it
+ // SIGTTOU which suspends it. if that background process is the session
+ // leader and doesn't have SIGTTOU blocked, the kernel will instead just
+ // return ENOTTY from the tcsetpgrp call rather than sending a signal to
+ // avoid deadlocking the process. therefore, we need to ensure that
+ // SIGTTOU is blocked here.
+
+ // Safety: setting a signal handler to SigIgn is always safe
+ unsafe {
+ nix::sys::signal::signal(
+ nix::sys::signal::Signal::SIGTTOU,
+ nix::sys::signal::SigHandler::SigIgn,
+ )?;
+ }
+ let res = nix::unistd::tcsetpgrp(pty, pg);
+ // Safety: setting a signal handler to SigDfl is always safe
+ unsafe {
+ nix::sys::signal::signal(
+ nix::sys::signal::Signal::SIGTTOU,
+ nix::sys::signal::SigHandler::SigDfl,
+ )?;
+ }
+ res?;
+
+ nix::unistd::close(pty)?;
+
+ nix::sys::signal::kill(neg_pid(pg), nix::sys::signal::Signal::SIGCONT)
+ // the process group has already exited
+ .allow(nix::errno::Errno::ESRCH)?;
+
+ Ok(())
+}
+
+pub fn setpgid_child(pg: Option<nix::unistd::Pid>) -> std::io::Result<()> {
+ nix::unistd::setpgid(PID0, pg.unwrap_or(PID0))?;
+ Ok(())
+}
+
+pub fn setpgid_parent(
+ pid: nix::unistd::Pid,
+ pg: Option<nix::unistd::Pid>,
+) -> Result<()> {
+ nix::unistd::setpgid(pid, pg.unwrap_or(PID0))
+ // the child already called exec, so it must have already called
+ // setpgid itself
+ .allow(nix::errno::Errno::EACCES)
+ // the child already exited, so we don't care
+ .allow(nix::errno::Errno::ESRCH)?;
+ Ok(())
+}
+
+pub fn id_to_pid(id: u32) -> nix::unistd::Pid {
+ nix::unistd::Pid::from_raw(id.try_into().unwrap())
+}
+
+pub fn neg_pid(pid: nix::unistd::Pid) -> nix::unistd::Pid {
+ nix::unistd::Pid::from_raw(-pid.as_raw())
+}
diff --git a/src/shell.pest b/src/shell.pest
index 0c63802..92b173a 100644
--- a/src/shell.pest
+++ b/src/shell.pest
@@ -23,8 +23,8 @@ alternation_bareword = @{ alternation_bareword_char+ }
alternation_word_part = ${
var |
alternation_bareword |
- "'" ~ single_string ~ "'" |
- "\"" ~ (var | double_string)+ ~ "\""
+ "'" ~ single_string? ~ "'" |
+ "\"" ~ (var | double_string)* ~ "\""
}
alternation_word = ${ alternation_word_part* }
alternation = ${ "{" ~ alternation_word ~ ("," ~ alternation_word)* ~ "}" }
@@ -36,8 +36,8 @@ word_part = ${
substitution |
var |
bareword |
- "'" ~ single_string ~ "'" |
- "\"" ~ (substitution | var | double_string)+ ~ "\""
+ "'" ~ single_string? ~ "'" |
+ "\"" ~ (substitution | var | double_string)* ~ "\""
}
word = ${ word_part+ }
diff --git a/src/shell/event.rs b/src/shell/event.rs
index 025f3c4..dc58e6f 100644
--- a/src/shell/event.rs
+++ b/src/shell/event.rs
@@ -1,53 +1,87 @@
+use crate::prelude::*;
+
#[derive(Debug)]
pub enum Event {
Key(textmode::Key),
Resize((u16, u16)),
PtyOutput,
- PtyClose,
ChildRunPipeline(usize, (usize, usize)),
ChildSuspend(usize),
- GitInfo(Option<super::git::Info>),
+ ChildExit(usize, super::history::ExitInfo, Option<Env>),
+ GitInfo(Option<super::inputs::GitInfo>),
ClockTimer,
}
-pub struct Reader {
- pending: async_std::sync::Mutex<Pending>,
- cvar: async_std::sync::Condvar,
+pub fn channel() -> (Writer, Reader) {
+ let (event_w, event_r) = tokio::sync::mpsc::unbounded_channel();
+ (Writer::new(event_w), Reader::new(event_r))
+}
+
+#[derive(Clone)]
+pub struct Writer(tokio::sync::mpsc::UnboundedSender<Event>);
+
+impl Writer {
+ pub fn new(event_w: tokio::sync::mpsc::UnboundedSender<Event>) -> Self {
+ Self(event_w)
+ }
+
+ pub fn send(&self, event: Event) {
+ // the only time this should ever error is when the application is
+ // shutting down, at which point we don't actually care about any
+ // further dropped messages
+ #[allow(clippy::let_underscore_drop)]
+ let _ = self.0.send(event);
+ }
}
+pub struct Reader(std::sync::Arc<InnerReader>);
+
impl Reader {
pub fn new(
- input: async_std::channel::Receiver<Event>,
- ) -> async_std::sync::Arc<Self> {
- let this = async_std::sync::Arc::new(Self {
- pending: async_std::sync::Mutex::new(Pending::new()),
- cvar: async_std::sync::Condvar::new(),
- });
+ mut input: tokio::sync::mpsc::UnboundedReceiver<Event>,
+ ) -> Self {
+ let inner = std::sync::Arc::new(InnerReader::new());
{
- let this = async_std::sync::Arc::clone(&this);
- async_std::task::spawn(async move {
- while let Ok(event) = input.recv().await {
- this.new_event(Some(event)).await;
+ let inner = inner.clone();
+ tokio::spawn(async move {
+ while let Some(event) = input.recv().await {
+ inner.new_event(Some(event));
}
- this.new_event(None).await;
+ inner.new_event(None);
});
}
- this
+ Self(inner)
}
pub async fn recv(&self) -> Option<Event> {
- let mut pending = self
- .cvar
- .wait_until(self.pending.lock().await, |pending| {
- pending.has_event()
- })
- .await;
- pending.get_event()
+ self.0.recv().await
+ }
+}
+
+struct InnerReader {
+ pending: std::sync::Mutex<Pending>,
+ cvar: tokio::sync::Notify,
+}
+
+impl InnerReader {
+ fn new() -> Self {
+ Self {
+ pending: std::sync::Mutex::new(Pending::new()),
+ cvar: tokio::sync::Notify::new(),
+ }
+ }
+
+ async fn recv(&self) -> Option<Event> {
+ loop {
+ if let Some(event) = self.pending.lock().unwrap().get_event() {
+ return event;
+ }
+ self.cvar.notified().await;
+ }
}
- async fn new_event(&self, event: Option<Event>) {
- let mut pending = self.pending.lock().await;
- pending.new_event(event);
+ fn new_event(&self, event: Option<Event>) {
+ self.pending.lock().unwrap().new_event(event);
self.cvar.notify_one();
}
}
@@ -58,10 +92,10 @@ struct Pending {
key: std::collections::VecDeque<textmode::Key>,
size: Option<(u16, u16)>,
pty_output: bool,
- pty_close: bool,
child_run_pipeline: std::collections::VecDeque<(usize, (usize, usize))>,
child_suspend: std::collections::VecDeque<usize>,
- git_info: Option<Option<super::git::Info>>,
+ child_exit: Option<(usize, super::history::ExitInfo, Option<Env>)>,
+ git_info: Option<Option<super::inputs::GitInfo>>,
clock_timer: bool,
done: bool,
}
@@ -71,53 +105,40 @@ impl Pending {
Self::default()
}
- fn has_event(&self) -> bool {
- self.done
- || !self.key.is_empty()
- || self.size.is_some()
- || self.pty_output
- || self.pty_close
- || !self.child_run_pipeline.is_empty()
- || !self.child_suspend.is_empty()
- || self.git_info.is_some()
- || self.clock_timer
- }
-
- fn get_event(&mut self) -> Option<Event> {
+ fn get_event(&mut self) -> Option<Option<Event>> {
if self.done {
- return None;
+ return Some(None);
}
if let Some(key) = self.key.pop_front() {
- return Some(Event::Key(key));
+ return Some(Some(Event::Key(key)));
}
if let Some(size) = self.size.take() {
- return Some(Event::Resize(size));
- }
- if self.pty_close {
- self.pty_close = false;
- return Some(Event::PtyClose);
+ return Some(Some(Event::Resize(size)));
}
if let Some((idx, span)) = self.child_run_pipeline.pop_front() {
- return Some(Event::ChildRunPipeline(idx, span));
+ return Some(Some(Event::ChildRunPipeline(idx, span)));
}
if let Some(idx) = self.child_suspend.pop_front() {
- return Some(Event::ChildSuspend(idx));
+ return Some(Some(Event::ChildSuspend(idx)));
+ }
+ if let Some((idx, exit_info, env)) = self.child_exit.take() {
+ return Some(Some(Event::ChildExit(idx, exit_info, env)));
}
if let Some(info) = self.git_info.take() {
- return Some(Event::GitInfo(info));
+ return Some(Some(Event::GitInfo(info)));
}
if self.clock_timer {
self.clock_timer = false;
- return Some(Event::ClockTimer);
+ return Some(Some(Event::ClockTimer));
}
// process_output should be last because it will often be the case
// that there is ~always new process output (cat on large files, yes,
// etc) and that shouldn't prevent other events from happening
if self.pty_output {
self.pty_output = false;
- return Some(Event::PtyOutput);
+ return Some(Some(Event::PtyOutput));
}
- unreachable!()
+ None
}
fn new_event(&mut self, event: Option<Event>) {
@@ -125,13 +146,15 @@ impl Pending {
Some(Event::Key(key)) => self.key.push_back(key),
Some(Event::Resize(size)) => self.size = Some(size),
Some(Event::PtyOutput) => self.pty_output = true,
- Some(Event::PtyClose) => self.pty_close = true,
Some(Event::ChildRunPipeline(idx, span)) => {
self.child_run_pipeline.push_back((idx, span));
}
Some(Event::ChildSuspend(idx)) => {
self.child_suspend.push_back(idx);
}
+ Some(Event::ChildExit(idx, exit_info, env)) => {
+ self.child_exit = Some((idx, exit_info, env));
+ }
Some(Event::GitInfo(info)) => self.git_info = Some(info),
Some(Event::ClockTimer) => self.clock_timer = true,
None => self.done = true,
diff --git a/src/shell/history/entry.rs b/src/shell/history/entry.rs
index a45d99d..0491bf7 100644
--- a/src/shell/history/entry.rs
+++ b/src/shell/history/entry.rs
@@ -1,25 +1,13 @@
use crate::shell::prelude::*;
-enum State {
- Running((usize, usize)),
- Exited(ExitInfo),
-}
-
pub struct Entry {
cmdline: String,
env: Env,
- state: State,
- vt: vt100::Parser,
- audible_bell_state: usize,
- visual_bell_state: usize,
- audible_bell: bool,
- visual_bell: bool,
- real_bell_pending: bool,
+ pty: super::pty::Pty,
fullscreen: Option<bool>,
- input: async_std::channel::Sender<Vec<u8>>,
- resize: async_std::channel::Sender<(u16, u16)>,
- start_time: time::OffsetDateTime,
start_instant: std::time::Instant,
+ start_time: time::OffsetDateTime,
+ state: State,
}
impl Entry {
@@ -27,39 +15,37 @@ impl Entry {
cmdline: String,
env: Env,
size: (u16, u16),
- input: async_std::channel::Sender<Vec<u8>>,
- resize: async_std::channel::Sender<(u16, u16)>,
- ) -> Self {
- let span = (0, cmdline.len());
- Self {
+ event_w: crate::shell::event::Writer,
+ ) -> Result<Self> {
+ let start_instant = std::time::Instant::now();
+ let start_time = time::OffsetDateTime::now_utc();
+
+ let (pty, pts) = super::pty::Pty::new(size, event_w.clone()).unwrap();
+ let (child, fh) = Self::spawn_command(&cmdline, &env, &pts)?;
+ tokio::spawn(Self::task(child, fh, env.idx(), event_w));
+ Ok(Self {
cmdline,
env,
- state: State::Running(span),
- vt: vt100::Parser::new(size.0, size.1, 0),
- audible_bell_state: 0,
- visual_bell_state: 0,
- audible_bell: false,
- visual_bell: false,
- real_bell_pending: false,
- input,
- resize,
+ pty,
fullscreen: None,
- start_time: time::OffsetDateTime::now_utc(),
- start_instant: std::time::Instant::now(),
- }
+ start_instant,
+ start_time,
+ state: State::Running((0, 0)),
+ })
}
pub fn render(
- &mut self,
+ &self,
out: &mut impl textmode::Textmode,
- idx: usize,
entry_count: usize,
- size: (u16, u16),
+ vt: &mut super::pty::Vt,
focused: bool,
scrolling: bool,
offset: time::UtcOffset,
) {
- let time = self.exit_info().map_or_else(
+ let idx = self.env.idx();
+ let size = out.screen().size();
+ let time = self.state.exit_info().map_or_else(
|| {
format!(
"[{}]",
@@ -77,13 +63,11 @@ impl Entry {
},
);
- self.bell(out);
- if focused {
- self.audible_bell = false;
- self.visual_bell = false;
+ if vt.bell(focused) {
+ out.write(b"\x07");
}
- set_bgcolor(out, idx, focused);
+ Self::set_bgcolor(out, idx, focused);
out.set_fgcolor(textmode::color::YELLOW);
let entry_count_width = format!("{}", entry_count + 1).len();
let idx_str = format!("{}", idx + 1);
@@ -92,8 +76,8 @@ impl Entry {
out.write_str(" ");
out.reset_attributes();
- set_bgcolor(out, idx, focused);
- if let Some(info) = self.exit_info() {
+ Self::set_bgcolor(out, idx, focused);
+ if let Some(info) = self.state.exit_info() {
if info.status.signal().is_some() {
out.set_fgcolor(textmode::color::MAGENTA);
} else if info.status.success() {
@@ -107,13 +91,13 @@ impl Entry {
}
out.reset_attributes();
- if self.audible_bell || self.visual_bell {
+ if vt.is_bell() {
out.set_bgcolor(textmode::Color::Rgb(64, 16, 16));
} else {
- set_bgcolor(out, idx, focused);
+ Self::set_bgcolor(out, idx, focused);
}
out.write_str("$ ");
- set_bgcolor(out, idx, focused);
+ Self::set_bgcolor(out, idx, focused);
let start = usize::from(out.screen().cursor_position().1);
let end = usize::from(size.1) - time.len() - 2;
let max_len = end - start;
@@ -130,7 +114,7 @@ impl Entry {
if !cmd[span.0..span.1].is_empty() {
out.set_bgcolor(textmode::Color::Rgb(16, 64, 16));
out.write_str(&cmd[span.0..span.1]);
- set_bgcolor(out, idx, focused);
+ Self::set_bgcolor(out, idx, focused);
}
if !cmd[span.1..].is_empty() {
out.write_str(&cmd[span.1..]);
@@ -155,7 +139,7 @@ impl Entry {
}
out.reset_attributes();
- set_bgcolor(out, idx, focused);
+ Self::set_bgcolor(out, idx, focused);
let cur_pos = out.screen().cursor_position();
out.write_str(&" ".repeat(
usize::from(size.1) - time.len() - 1 - usize::from(cur_pos.1),
@@ -164,7 +148,7 @@ impl Entry {
out.write_str(" ");
out.reset_attributes();
- if self.binary() {
+ if vt.binary() {
let msg = "This appears to be binary data. Fullscreen this entry to view anyway.";
let len: u16 = msg.len().try_into().unwrap();
out.move_to(
@@ -175,7 +159,8 @@ impl Entry {
out.write_str(msg);
out.hide_cursor(true);
} else {
- let last_row = self.output_lines(focused && !scrolling);
+ let last_row =
+ vt.output_lines(focused && !scrolling, self.state.running());
let mut max_lines = self.max_lines(entry_count);
if last_row > max_lines {
out.write(b"\r\n");
@@ -185,7 +170,7 @@ impl Entry {
max_lines -= 1;
}
let mut out_row = out.screen().cursor_position().0 + 1;
- let screen = self.vt.screen();
+ let screen = vt.screen();
let pos = screen.cursor_position();
let mut wrapped = false;
let mut cursor_found = None;
@@ -216,66 +201,41 @@ impl Entry {
}
}
}
- out.reset_attributes();
- }
- pub fn render_fullscreen(&mut self, out: &mut impl textmode::Textmode) {
- out.write(&self.vt.screen().state_formatted());
- self.bell(out);
- self.audible_bell = false;
- self.visual_bell = false;
out.reset_attributes();
}
- pub async fn send_input(&self, bytes: Vec<u8>) {
- if self.running() {
- self.input.send(bytes).await.unwrap();
- }
- }
-
- pub async fn resize(&mut self, size: (u16, u16)) {
- if self.running() {
- self.resize.send(size).await.unwrap();
- self.vt.set_size(size.0, size.1);
- }
+ pub fn render_fullscreen(&self, out: &mut impl textmode::Textmode) {
+ self.pty.with_vt_mut(|vt| {
+ out.write(&vt.screen().state_formatted());
+ if vt.bell(true) {
+ out.write(b"\x07");
+ }
+ out.reset_attributes();
+ });
}
- pub fn size(&self) -> (u16, u16) {
- self.vt.screen().size()
+ pub fn input(&self, bytes: Vec<u8>) {
+ self.pty.input(bytes);
}
- pub fn process(&mut self, input: &[u8]) {
- self.vt.process(input);
- let screen = self.vt.screen();
-
- let new_audible_bell_state = screen.audible_bell_count();
- if new_audible_bell_state != self.audible_bell_state {
- self.audible_bell = true;
- self.real_bell_pending = true;
- self.audible_bell_state = new_audible_bell_state;
- }
-
- let new_visual_bell_state = screen.visual_bell_count();
- if new_visual_bell_state != self.visual_bell_state {
- self.visual_bell = true;
- self.real_bell_pending = true;
- self.visual_bell_state = new_visual_bell_state;
- }
+ pub fn resize(&self, size: (u16, u16)) {
+ self.pty.resize(size);
}
pub fn cmd(&self) -> &str {
&self.cmdline
}
- pub fn env(&self) -> &Env {
- &self.env
+ pub fn start_time(&self) -> time::OffsetDateTime {
+ self.start_time
}
pub fn toggle_fullscreen(&mut self) {
if let Some(fullscreen) = self.fullscreen {
self.fullscreen = Some(!fullscreen);
} else {
- self.fullscreen = Some(!self.vt.screen().alternate_screen());
+ self.fullscreen = Some(!self.pty.fullscreen());
}
}
@@ -284,110 +244,186 @@ impl Entry {
}
pub fn running(&self) -> bool {
- matches!(self.state, State::Running(_))
+ self.state.running()
}
- pub fn binary(&self) -> bool {
- self.vt.screen().errors() > 5
+ pub fn exited(&mut self, exit_info: ExitInfo) {
+ self.state = State::Exited(exit_info);
}
pub fn lines(&self, entry_count: usize, focused: bool) -> usize {
+ let running = self.running();
1 + std::cmp::min(
- self.output_lines(focused),
+ self.pty.with_vt(|vt| vt.output_lines(focused, running)),
self.max_lines(entry_count),
)
}
+ pub fn should_fullscreen(&self) -> bool {
+ self.fullscreen.unwrap_or_else(|| self.pty.fullscreen())
+ }
+
+ pub fn lock_vt(&self) -> std::sync::MutexGuard<super::pty::Vt> {
+ self.pty.lock_vt()
+ }
+
+ pub fn set_span(&mut self, new_span: (usize, usize)) {
+ if let State::Running(ref mut span) = self.state {
+ *span = new_span;
+ }
+ }
+
fn max_lines(&self, entry_count: usize) -> usize {
if self.env.idx() == entry_count - 1 {
- usize::from(self.size().0) * 2 / 3
+ 15
} else {
5
}
}
- pub fn output_lines(&self, focused: bool) -> usize {
- if self.binary() {
- return 1;
+ fn set_bgcolor(
+ out: &mut impl textmode::Textmode,
+ idx: usize,
+ focus: bool,
+ ) {
+ if focus {
+ out.set_bgcolor(textmode::Color::Rgb(0x56, 0x1b, 0x8b));
+ } else if idx % 2 == 0 {
+ out.set_bgcolor(textmode::Color::Rgb(0x24, 0x21, 0x00));
+ } else {
+ out.set_bgcolor(textmode::Color::Rgb(0x20, 0x20, 0x20));
}
+ }
- let screen = self.vt.screen();
- let mut last_row = 0;
- for (idx, row) in screen.rows(0, self.size().1).enumerate() {
- if !row.is_empty() {
- last_row = idx + 1;
- }
+ fn spawn_command(
+ cmdline: &str,
+ env: &Env,
+ pts: &pty_process::Pts,
+ ) -> Result<(tokio::process::Child, std::fs::File)> {
+ let mut cmd = pty_process::Command::new(crate::info::current_exe()?);
+ cmd.args(&["-c", cmdline, "--status-fd", "3"]);
+ env.apply(&mut cmd);
+ let (from_r, from_w) =
+ nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
+ // Safety: from_r was just opened above and is not used anywhere else
+ let fh = unsafe { std::fs::File::from_raw_fd(from_r) };
+ // Safety: dup2 is an async-signal-safe function
+ unsafe {
+ cmd.pre_exec(move || {
+ nix::unistd::dup2(from_w, 3)?;
+ Ok(())
+ });
}
- if focused && self.running() {
- last_row = std::cmp::max(
- last_row,
- usize::from(screen.cursor_position().0) + 1,
- );
- }
- last_row
+ let child = cmd.spawn(pts)?;
+ nix::unistd::close(from_w)?;
+ Ok((child, fh))
}
- pub fn should_fullscreen(&self) -> bool {
- self.fullscreen
- .unwrap_or_else(|| self.vt.screen().alternate_screen())
- }
+ async fn task(
+ mut child: tokio::process::Child,
+ fh: std::fs::File,
+ idx: usize,
+ event_w: crate::shell::event::Writer,
+ ) {
+ enum Res {
+ Read(crate::runner::Event),
+ Exit(std::io::Result<std::process::ExitStatus>),
+ }
- pub fn set_span(&mut self, span: (usize, usize)) {
- if matches!(self.state, State::Running(_)) {
- self.state = State::Running(span);
+ let (read_w, read_r) = tokio::sync::mpsc::unbounded_channel();
+ tokio::task::spawn_blocking(move || loop {
+ let event = bincode::deserialize_from(&fh);
+ match event {
+ Ok(event) => {
+ read_w.send(event).unwrap();
+ }
+ Err(e) => {
+ match &*e {
+ bincode::ErrorKind::Io(io_e) => {
+ assert!(
+ io_e.kind()
+ == std::io::ErrorKind::UnexpectedEof
+ );
+ }
+ e => {
+ panic!("{}", e);
+ }
+ }
+ break;
+ }
+ }
+ });
+
+ let mut stream: futures_util::stream::SelectAll<_> = [
+ tokio_stream::wrappers::UnboundedReceiverStream::new(read_r)
+ .map(Res::Read)
+ .boxed(),
+ futures_util::stream::once(child.wait())
+ .map(Res::Exit)
+ .boxed(),
+ ]
+ .into_iter()
+ .collect();
+ let mut exit_status = None;
+ let mut new_env = None;
+ while let Some(res) = stream.next().await {
+ match res {
+ Res::Read(event) => match event {
+ crate::runner::Event::RunPipeline(new_span) => {
+ // we could just update the span in place here, but we
+ // do this as an event so that we can also trigger a
+ // refresh
+ event_w.send(Event::ChildRunPipeline(idx, new_span));
+ }
+ crate::runner::Event::Suspend => {
+ event_w.send(Event::ChildSuspend(idx));
+ }
+ crate::runner::Event::Exit(env) => {
+ new_env = Some(env);
+ }
+ },
+ Res::Exit(status) => {
+ exit_status = Some(status.unwrap());
+ }
+ }
}
+ event_w.send(Event::ChildExit(
+ idx,
+ ExitInfo::new(exit_status.unwrap()),
+ new_env,
+ ));
}
+}
- pub async fn finish(
- &mut self,
- env: Env,
- event_w: async_std::channel::Sender<Event>,
- ) {
- self.state = State::Exited(ExitInfo::new(env.latest_status()));
- self.env = env;
- event_w.send(Event::PtyClose).await.unwrap();
- }
+enum State {
+ Running((usize, usize)),
+ Exited(ExitInfo),
+}
+impl State {
fn exit_info(&self) -> Option<&ExitInfo> {
- match &self.state {
- State::Running(..) => None,
- State::Exited(exit_info) => Some(exit_info),
+ match self {
+ Self::Running(_) => None,
+ Self::Exited(exit_info) => Some(exit_info),
}
}
- fn bell(&mut self, out: &mut impl textmode::Textmode) {
- if self.real_bell_pending {
- if self.audible_bell {
- out.write(b"\x07");
- }
- if self.visual_bell {
- out.write(b"\x1bg");
- }
- self.real_bell_pending = false;
- }
+ fn running(&self) -> bool {
+ self.exit_info().is_none()
}
}
-struct ExitInfo {
- status: async_std::process::ExitStatus,
+#[derive(Debug)]
+pub struct ExitInfo {
+ status: std::process::ExitStatus,
instant: std::time::Instant,
}
impl ExitInfo {
- fn new(status: async_std::process::ExitStatus) -> Self {
+ fn new(status: std::process::ExitStatus) -> Self {
Self {
status,
instant: std::time::Instant::now(),
}
}
}
-
-fn set_bgcolor(out: &mut impl textmode::Textmode, idx: usize, focus: bool) {
- if focus {
- out.set_bgcolor(textmode::Color::Rgb(0x56, 0x1b, 0x8b));
- } else if idx % 2 == 0 {
- out.set_bgcolor(textmode::Color::Rgb(0x24, 0x21, 0x00));
- } else {
- out.set_bgcolor(textmode::Color::Rgb(0x20, 0x20, 0x20));
- }
-}
diff --git a/src/shell/history/mod.rs b/src/shell/history/mod.rs
index 1bc4e62..91149c1 100644
--- a/src/shell/history/mod.rs
+++ b/src/shell/history/mod.rs
@@ -1,12 +1,12 @@
use crate::shell::prelude::*;
mod entry;
-pub use entry::Entry;
+pub use entry::{Entry, ExitInfo};
mod pty;
pub struct History {
size: (u16, u16),
- entries: Vec<crate::mutex::Mutex<Entry>>,
+ entries: Vec<Entry>,
scroll_pos: usize,
}
@@ -19,31 +19,27 @@ impl History {
}
}
- pub async fn render(
+ pub fn render(
&self,
out: &mut impl textmode::Textmode,
repl_lines: usize,
focus: Option<usize>,
scrolling: bool,
offset: time::UtcOffset,
- ) -> anyhow::Result<()> {
- let mut used_lines = repl_lines;
+ ) {
let mut cursor = None;
- for (idx, mut entry) in
- self.visible(repl_lines, focus, scrolling).await.rev()
+ for (idx, used_lines, mut vt) in
+ self.visible(repl_lines, focus, scrolling).rev()
{
let focused = focus.map_or(false, |focus| idx == focus);
- used_lines +=
- entry.lines(self.entry_count(), focused && !scrolling);
out.move_to(
(usize::from(self.size.0) - used_lines).try_into().unwrap(),
0,
);
- entry.render(
+ self.entries[idx].render(
out,
- idx,
self.entry_count(),
- self.size,
+ &mut *vt,
focused,
scrolling,
offset,
@@ -59,67 +55,38 @@ impl History {
out.move_to(pos.0, pos.1);
out.hide_cursor(hide);
}
- Ok(())
}
- pub async fn render_fullscreen(
- &self,
- out: &mut impl textmode::Textmode,
- idx: usize,
- ) {
- let mut entry = self.entries[idx].lock_arc().await;
- entry.render_fullscreen(out);
+ pub fn entry(&self, idx: usize) -> &Entry {
+ &self.entries[idx]
}
- pub async fn send_input(&mut self, idx: usize, input: Vec<u8>) {
- self.entry(idx).await.send_input(input).await;
+ pub fn entry_mut(&mut self, idx: usize) -> &mut Entry {
+ &mut self.entries[idx]
}
- pub async fn resize(&mut self, size: (u16, u16)) {
+ pub fn resize(&mut self, size: (u16, u16)) {
self.size = size;
for entry in &self.entries {
- entry.lock_arc().await.resize(size).await;
+ entry.resize(size);
}
}
- pub async fn run(
+ pub fn run(
&mut self,
- cmdline: &str,
- env: &Env,
- event_w: async_std::channel::Sender<Event>,
- ) -> anyhow::Result<usize> {
- let (input_w, input_r) = async_std::channel::unbounded();
- let (resize_w, resize_r) = async_std::channel::unbounded();
-
- let entry = crate::mutex::new(Entry::new(
- cmdline.to_string(),
- env.clone(),
- self.size,
- input_w,
- resize_w,
- ));
- run_commands(
- cmdline.to_string(),
- crate::mutex::clone(&entry),
- env.clone(),
- input_r,
- resize_r,
- event_w,
- );
-
- self.entries.push(entry);
- Ok(self.entries.len() - 1)
- }
-
- pub async fn entry(&self, idx: usize) -> crate::mutex::Guard<Entry> {
- self.entries[idx].lock_arc().await
+ cmdline: String,
+ env: Env,
+ event_w: crate::shell::event::Writer,
+ ) {
+ self.entries
+ .push(Entry::new(cmdline, env, self.size, event_w).unwrap());
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
- pub async fn make_focus_visible(
+ pub fn make_focus_visible(
&mut self,
repl_lines: usize,
focus: Option<usize>,
@@ -134,8 +101,7 @@ impl History {
while focus
< self
.visible(repl_lines, Some(focus), scrolling)
- .await
- .map(|(idx, _)| idx)
+ .map(|(idx, ..)| idx)
.next()
.unwrap()
{
@@ -149,8 +115,7 @@ impl History {
while focus
> self
.visible(repl_lines, Some(focus), scrolling)
- .await
- .map(|(idx, _)| idx)
+ .map(|(idx, ..)| idx)
.last()
.unwrap()
{
@@ -158,225 +123,86 @@ impl History {
}
}
- async fn visible(
+ pub async fn save(&self) {
+ // TODO: we'll probably want some amount of flock or something here
+ let mut fh = tokio::fs::OpenOptions::new()
+ .append(true)
+ .open(crate::dirs::history_file())
+ .await
+ .unwrap();
+ for entry in &self.entries {
+ fh.write_all(
+ format!(
+ ": {}:0;{}\n",
+ entry.start_time().unix_timestamp(),
+ entry.cmd()
+ )
+ .as_bytes(),
+ )
+ .await
+ .unwrap();
+ }
+ }
+
+ fn visible(
&self,
repl_lines: usize,
focus: Option<usize>,
scrolling: bool,
) -> VisibleEntries {
let mut iter = VisibleEntries::new();
- if self.entries.is_empty() {
- return iter;
- }
-
let mut used_lines = repl_lines;
for (idx, entry) in
self.entries.iter().enumerate().rev().skip(self.scroll_pos)
{
- let entry = entry.lock_arc().await;
let focused = focus.map_or(false, |focus| idx == focus);
used_lines +=
entry.lines(self.entry_count(), focused && !scrolling);
if used_lines > usize::from(self.size.0) {
break;
}
- iter.add(idx, entry);
+ iter.add(idx, used_lines, entry.lock_vt());
}
iter
}
}
-struct VisibleEntries {
- entries: std::collections::VecDeque<(usize, crate::mutex::Guard<Entry>)>,
+struct VisibleEntries<'a> {
+ entries: std::collections::VecDeque<(
+ usize,
+ usize,
+ std::sync::MutexGuard<'a, pty::Vt>,
+ )>,
}
-impl VisibleEntries {
+impl<'a> VisibleEntries<'a> {
fn new() -> Self {
Self {
entries: std::collections::VecDeque::new(),
}
}
- fn add(&mut self, idx: usize, entry: crate::mutex::Guard<Entry>) {
+ fn add(
+ &mut self,
+ idx: usize,
+ offset: usize,
+ vt: std::sync::MutexGuard<'a, pty::Vt>,
+ ) {
// push_front because we are adding them in reverse order
- self.entries.push_front((idx, entry));
+ self.entries.push_front((idx, offset, vt));
}
}
-impl std::iter::Iterator for VisibleEntries {
- type Item = (usize, crate::mutex::Guard<Entry>);
+impl<'a> std::iter::Iterator for VisibleEntries<'a> {
+ type Item = (usize, usize, std::sync::MutexGuard<'a, pty::Vt>);
fn next(&mut self) -> Option<Self::Item> {
self.entries.pop_front()
}
}
-impl std::iter::DoubleEndedIterator for VisibleEntries {
+impl<'a> std::iter::DoubleEndedIterator for VisibleEntries<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.entries.pop_back()
}
}
-
-fn run_commands(
- cmdline: String,
- entry: crate::mutex::Mutex<Entry>,
- mut env: Env,
- input_r: async_std::channel::Receiver<Vec<u8>>,
- resize_r: async_std::channel::Receiver<(u16, u16)>,
- event_w: async_std::channel::Sender<Event>,
-) {
- async_std::task::spawn(async move {
- let pty = match pty::Pty::new(
- entry.lock_arc().await.size(),
- &entry,
- input_r,
- resize_r,
- event_w.clone(),
- ) {
- Ok(pty) => pty,
- Err(e) => {
- let mut entry = entry.lock_arc().await;
- entry.process(
- format!("nbsh: failed to allocate pty: {}\r\n", e)
- .as_bytes(),
- );
- env.set_status(async_std::process::ExitStatus::from_raw(
- 1 << 8,
- ));
- entry.finish(env, event_w).await;
- return;
- }
- };
-
- let status =
- match spawn_commands(&cmdline, &pty, &mut env, event_w.clone())
- .await
- {
- Ok(status) => status,
- Err(e) => {
- let mut entry = entry.lock_arc().await;
- entry.process(
- format!(
- "nbsh: failed to spawn {}: {}\r\n",
- cmdline, e
- )
- .as_bytes(),
- );
- env.set_status(async_std::process::ExitStatus::from_raw(
- 1 << 8,
- ));
- entry.finish(env, event_w).await;
- return;
- }
- };
- env.set_status(status);
-
- entry.lock_arc().await.finish(env, event_w).await;
- pty.close().await;
- });
-}
-
-async fn spawn_commands(
- cmdline: &str,
- pty: &pty::Pty,
- env: &mut Env,
- event_w: async_std::channel::Sender<Event>,
-) -> anyhow::Result<async_std::process::ExitStatus> {
- let mut cmd = pty_process::Command::new(std::env::current_exe()?);
- cmd.args(&["-c", cmdline, "--status-fd", "3"]);
- env.apply(&mut cmd);
- let (from_r, from_w) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
- // Safety: dup2 is an async-signal-safe function
- unsafe {
- cmd.pre_exec(move || {
- nix::unistd::dup2(from_w, 3)?;
- Ok(())
- });
- }
- let child = pty.spawn(cmd)?;
- nix::unistd::close(from_w)?;
-
- let (read_w, read_r) = async_std::channel::unbounded();
- let new_read = move || {
- let read_w = read_w.clone();
- async_std::task::spawn(async move {
- let event = blocking::unblock(move || {
- // Safety: from_r was just opened above and is only
- // referenced in this closure, which takes ownership of it
- // at the start and returns ownership of it at the end
- let fh = unsafe { std::fs::File::from_raw_fd(from_r) };
- let event = bincode::deserialize_from(&fh);
- let _ = fh.into_raw_fd();
- event
- })
- .await;
- if read_w.is_closed() {
- // we should never drop read_r while there are still valid
- // things to read
- assert!(event.is_err());
- } else {
- read_w.send(event).await.unwrap();
- }
- });
- };
-
- new_read();
- let mut read_done = false;
- let mut exit_done = None;
- loop {
- enum Res {
- Read(bincode::Result<crate::runner::Event>),
- Exit(std::io::Result<std::process::ExitStatus>),
- }
-
- let read_r = read_r.clone();
- let read = async move { Res::Read(read_r.recv().await.unwrap()) };
- let exit = async {
- Res::Exit(if exit_done.is_none() {
- child.status_no_drop().await
- } else {
- std::future::pending().await
- })
- };
- match read.or(exit).await {
- Res::Read(Ok(event)) => match event {
- crate::runner::Event::RunPipeline(idx, span) => {
- event_w
- .send(Event::ChildRunPipeline(idx, span))
- .await
- .unwrap();
- new_read();
- }
- crate::runner::Event::Suspend(idx) => {
- event_w.send(Event::ChildSuspend(idx)).await.unwrap();
- new_read();
- }
- crate::runner::Event::Exit(new_env) => {
- *env = new_env;
- read_done = true;
- }
- },
- Res::Read(Err(e)) => {
- if let bincode::ErrorKind::Io(io_e) = &*e {
- if io_e.kind() == std::io::ErrorKind::UnexpectedEof {
- read_done = true;
- } else {
- anyhow::bail!(e);
- }
- } else {
- anyhow::bail!(e);
- }
- }
- Res::Exit(Ok(status)) => {
- exit_done = Some(status);
- }
- Res::Exit(Err(e)) => {
- anyhow::bail!(e);
- }
- }
- if let (true, Some(status)) = (read_done, exit_done) {
- nix::unistd::close(from_r)?;
- return Ok(status);
- }
- }
-}
diff --git a/src/shell/history/pty.rs b/src/shell/history/pty.rs
index 0fe0942..cef4ca9 100644
--- a/src/shell/history/pty.rs
+++ b/src/shell/history/pty.rs
@@ -1,106 +1,196 @@
use crate::shell::prelude::*;
+#[derive(Debug)]
+enum Request {
+ Input(Vec<u8>),
+ Resize(u16, u16),
+}
+
pub struct Pty {
- pty: async_std::sync::Arc<pty_process::Pty>,
- close_w: async_std::channel::Sender<()>,
+ vt: std::sync::Arc<std::sync::Mutex<Vt>>,
+ request_w: tokio::sync::mpsc::UnboundedSender<Request>,
}
impl Pty {
pub fn new(
size: (u16, u16),
- entry: &crate::mutex::Mutex<super::Entry>,
- input_r: async_std::channel::Receiver<Vec<u8>>,
- resize_r: async_std::channel::Receiver<(u16, u16)>,
- event_w: async_std::channel::Sender<Event>,
- ) -> anyhow::Result<Self> {
- let (close_w, close_r) = async_std::channel::unbounded();
+ event_w: crate::shell::event::Writer,
+ ) -> Result<(Self, pty_process::Pts)> {
+ let (request_w, request_r) = tokio::sync::mpsc::unbounded_channel();
let pty = pty_process::Pty::new()?;
pty.resize(pty_process::Size::new(size.0, size.1))?;
- let pty = async_std::sync::Arc::new(pty);
-
- async_std::task::spawn(pty_task(
- async_std::sync::Arc::clone(&pty),
- crate::mutex::clone(entry),
- input_r,
- resize_r,
- close_r,
+ let pts = pty.pts()?;
+
+ let vt = std::sync::Arc::new(std::sync::Mutex::new(Vt::new(size)));
+
+ tokio::spawn(Self::task(
+ pty,
+ std::sync::Arc::clone(&vt),
+ request_r,
event_w,
));
- Ok(Self { pty, close_w })
+ Ok((Self { vt, request_w }, pts))
}
- pub fn spawn(
- &self,
- mut cmd: pty_process::Command,
- ) -> anyhow::Result<async_std::process::Child> {
- Ok(cmd.spawn(&self.pty)?)
+ pub fn with_vt<T>(&self, f: impl FnOnce(&Vt) -> T) -> T {
+ let vt = self.vt.lock().unwrap();
+ f(&*vt)
}
- pub async fn close(&self) {
- self.close_w.send(()).await.unwrap();
+ pub fn with_vt_mut<T>(&self, f: impl FnOnce(&mut Vt) -> T) -> T {
+ let mut vt = self.vt.lock().unwrap();
+ f(&mut *vt)
+ }
+
+ pub fn lock_vt(&self) -> std::sync::MutexGuard<Vt> {
+ self.vt.lock().unwrap()
+ }
+
+ pub fn fullscreen(&self) -> bool {
+ self.with_vt(|vt| vt.screen().alternate_screen())
+ }
+
+ pub fn input(&self, bytes: Vec<u8>) {
+ #[allow(clippy::let_underscore_drop)]
+ let _ = self.request_w.send(Request::Input(bytes));
+ }
+
+ pub fn resize(&self, size: (u16, u16)) {
+ #[allow(clippy::let_underscore_drop)]
+ let _ = self.request_w.send(Request::Resize(size.0, size.1));
}
-}
-async fn pty_task(
- pty: async_std::sync::Arc<pty_process::Pty>,
- entry: crate::mutex::Mutex<super::Entry>,
- input_r: async_std::channel::Receiver<Vec<u8>>,
- resize_r: async_std::channel::Receiver<(u16, u16)>,
- close_r: async_std::channel::Receiver<()>,
- event_w: async_std::channel::Sender<Event>,
-) {
- loop {
+ async fn task(
+ pty: pty_process::Pty,
+ vt: std::sync::Arc<std::sync::Mutex<Vt>>,
+ request_r: tokio::sync::mpsc::UnboundedReceiver<Request>,
+ event_w: crate::shell::event::Writer,
+ ) {
enum Res {
- Read(Result<usize, std::io::Error>),
- Write(Result<Vec<u8>, async_std::channel::RecvError>),
- Resize(Result<(u16, u16), async_std::channel::RecvError>),
- Close(Result<(), async_std::channel::RecvError>),
+ Read(Result<bytes::Bytes, std::io::Error>),
+ Request(Request),
}
- let mut buf = [0_u8; 4096];
- let read = async { Res::Read((&*pty).read(&mut buf).await) };
- let write = async { Res::Write(input_r.recv().await) };
- let resize = async { Res::Resize(resize_r.recv().await) };
- let close = async { Res::Close(close_r.recv().await) };
- match read.race(write).race(resize).or(close).await {
- Res::Read(res) => match res {
- Ok(bytes) => {
- entry.lock_arc().await.process(&buf[..bytes]);
- event_w.send(Event::PtyOutput).await.unwrap();
- }
- Err(e) => {
- if e.raw_os_error() != Some(libc::EIO) {
+
+ let (pty_r, mut pty_w) = pty.into_split();
+ let mut stream: futures_util::stream::SelectAll<_> = [
+ tokio_util::io::ReaderStream::new(pty_r)
+ .map(Res::Read)
+ .boxed(),
+ tokio_stream::wrappers::UnboundedReceiverStream::new(request_r)
+ .map(Res::Request)
+ .boxed(),
+ ]
+ .into_iter()
+ .collect();
+ while let Some(res) = stream.next().await {
+ match res {
+ Res::Read(res) => match res {
+ Ok(bytes) => {
+ vt.lock().unwrap().process(&bytes);
+ event_w.send(Event::PtyOutput);
+ }
+ Err(e) => {
+ // this means that there are no longer any open pts
+ // fds. we could alternately signal this through an
+ // explicit channel at ChildExit time, but this seems
+ // reliable enough.
+ if e.raw_os_error() == Some(libc::EIO) {
+ return;
+ }
panic!("pty read failed: {:?}", e);
}
+ },
+ Res::Request(Request::Input(bytes)) => {
+ pty_w.write(&bytes).await.unwrap();
}
- },
- Res::Write(res) => match res {
- Ok(bytes) => {
- (&*pty).write(&bytes).await.unwrap();
- }
- Err(e) => {
- panic!("failed to read from input channel: {}", e);
- }
- },
- Res::Resize(res) => match res {
- Ok(size) => {
- pty.resize(pty_process::Size::new(size.0, size.1))
- .unwrap();
- }
- Err(e) => {
- panic!("failed to read from resize channel: {}", e);
+ Res::Request(Request::Resize(row, col)) => {
+ pty_w.resize(pty_process::Size::new(row, col)).unwrap();
+ vt.lock().unwrap().set_size((row, col));
}
- },
- Res::Close(res) => match res {
- Ok(()) => {
- event_w.send(Event::PtyClose).await.unwrap();
- return;
- }
- Err(e) => {
- panic!("failed to read from close channel: {}", e);
- }
- },
+ }
+ }
+ }
+}
+
+pub struct Vt {
+ vt: vt100::Parser,
+ bell_state: usize,
+ bell: bool,
+ real_bell_pending: bool,
+}
+
+impl Vt {
+ pub fn new(size: (u16, u16)) -> Self {
+ Self {
+ vt: vt100::Parser::new(size.0, size.1, 0),
+ bell_state: 0,
+ bell: false,
+ real_bell_pending: false,
+ }
+ }
+
+ pub fn process(&mut self, bytes: &[u8]) {
+ self.vt.process(bytes);
+ let screen = self.vt.screen();
+
+ let new_bell_state = screen.audible_bell_count();
+ if new_bell_state != self.bell_state {
+ self.bell = true;
+ self.real_bell_pending = true;
+ self.bell_state = new_bell_state;
+ }
+ }
+
+ pub fn screen(&self) -> &vt100::Screen {
+ self.vt.screen()
+ }
+
+ pub fn set_size(&mut self, size: (u16, u16)) {
+ self.vt.set_size(size.0, size.1);
+ }
+
+ pub fn is_bell(&self) -> bool {
+ self.bell
+ }
+
+ pub fn bell(&mut self, focused: bool) -> bool {
+ let mut should = false;
+ if self.real_bell_pending {
+ if self.bell {
+ should = true;
+ }
+ self.real_bell_pending = false;
+ }
+ if focused {
+ self.bell = false;
+ }
+ should
+ }
+
+ pub fn binary(&self) -> bool {
+ self.vt.screen().errors() > 5
+ }
+
+ pub fn output_lines(&self, focused: bool, running: bool) -> usize {
+ if self.binary() {
+ return 1;
+ }
+
+ let screen = self.vt.screen();
+ let mut last_row = 0;
+ for (idx, row) in screen.rows(0, screen.size().1).enumerate() {
+ if !row.is_empty() {
+ last_row = idx + 1;
+ }
+ }
+ if focused && running {
+ last_row = std::cmp::max(
+ last_row,
+ usize::from(screen.cursor_position().0) + 1,
+ );
}
+ last_row
}
}
diff --git a/src/shell/inputs/clock.rs b/src/shell/inputs/clock.rs
new file mode 100644
index 0000000..250466e
--- /dev/null
+++ b/src/shell/inputs/clock.rs
@@ -0,0 +1,27 @@
+use crate::shell::prelude::*;
+
+pub struct Handler;
+
+impl Handler {
+ pub fn new(event_w: crate::shell::event::Writer) -> Self {
+ tokio::spawn(Self::task(event_w));
+ Self
+ }
+
+ async fn task(event_w: crate::shell::event::Writer) {
+ let now_clock = time::OffsetDateTime::now_utc();
+ let now_instant = tokio::time::Instant::now();
+ let mut interval = tokio::time::interval_at(
+ now_instant
+ + std::time::Duration::from_nanos(
+ 1_000_000_000_u64
+ .saturating_sub(now_clock.nanosecond().into()),
+ ),
+ std::time::Duration::from_secs(1),
+ );
+ loop {
+ interval.tick().await;
+ event_w.send(Event::ClockTimer);
+ }
+ }
+}
diff --git a/src/shell/git.rs b/src/shell/inputs/git.rs
index 48e5eea..dbae1c4 100644
--- a/src/shell/git.rs
+++ b/src/shell/inputs/git.rs
@@ -1,3 +1,76 @@
+use crate::shell::prelude::*;
+
+use notify::Watcher as _;
+
+pub struct Handler {
+ git_w: tokio::sync::mpsc::UnboundedSender<std::path::PathBuf>,
+}
+
+impl Handler {
+ pub fn new(event_w: crate::shell::event::Writer) -> Self {
+ let (git_w, git_r) = tokio::sync::mpsc::unbounded_channel();
+ tokio::spawn(Self::task(git_r, event_w));
+ Self { git_w }
+ }
+
+ pub fn new_dir(&self, path: std::path::PathBuf) {
+ self.git_w.send(path).unwrap();
+ }
+
+ async fn task(
+ mut git_r: tokio::sync::mpsc::UnboundedReceiver<std::path::PathBuf>,
+ event_w: crate::shell::event::Writer,
+ ) {
+ // clippy can't tell that we assign to this later
+ #[allow(clippy::no_effect_underscore_binding)]
+ let mut _active_watcher = None;
+ while let Some(mut dir) = git_r.recv().await {
+ while let Ok(newer_dir) = git_r.try_recv() {
+ dir = newer_dir;
+ }
+ let repo = git2::Repository::discover(&dir).ok();
+ if repo.is_some() {
+ let (sync_watch_w, sync_watch_r) = std::sync::mpsc::channel();
+ let (watch_w, mut watch_r) =
+ tokio::sync::mpsc::unbounded_channel();
+ let mut watcher =
+ notify::recommended_watcher(sync_watch_w).unwrap();
+ watcher
+ .watch(&dir, notify::RecursiveMode::Recursive)
+ .unwrap();
+ tokio::task::spawn_blocking(move || {
+ while let Ok(event) = sync_watch_r.recv() {
+ if watch_w.send(event).is_err() {
+ break;
+ }
+ }
+ });
+ let event_w = event_w.clone();
+ tokio::spawn(async move {
+ while watch_r.recv().await.is_some() {
+ let repo = git2::Repository::discover(&dir).ok();
+ let info = tokio::task::spawn_blocking(|| {
+ repo.map(|repo| Info::new(&repo))
+ })
+ .await
+ .unwrap();
+ event_w.send(Event::GitInfo(info));
+ }
+ });
+ _active_watcher = Some(watcher);
+ } else {
+ _active_watcher = None;
+ }
+ let info = tokio::task::spawn_blocking(|| {
+ repo.map(|repo| Info::new(&repo))
+ })
+ .await
+ .unwrap();
+ event_w.send(Event::GitInfo(info));
+ }
+ }
+}
+
#[derive(Debug)]
pub struct Info {
modified_files: bool,
diff --git a/src/shell/inputs/mod.rs b/src/shell/inputs/mod.rs
new file mode 100644
index 0000000..48590a2
--- /dev/null
+++ b/src/shell/inputs/mod.rs
@@ -0,0 +1,32 @@
+use crate::shell::prelude::*;
+
+mod clock;
+mod git;
+pub use git::Info as GitInfo;
+mod signals;
+mod stdin;
+
+pub struct Handler {
+ _clock: clock::Handler,
+ git: git::Handler,
+ _signals: signals::Handler,
+ _stdin: stdin::Handler,
+}
+
+impl Handler {
+ pub fn new(
+ input: textmode::blocking::Input,
+ event_w: crate::shell::event::Writer,
+ ) -> Result<Self> {
+ Ok(Self {
+ _clock: clock::Handler::new(event_w.clone()),
+ git: git::Handler::new(event_w.clone()),
+ _signals: signals::Handler::new(event_w.clone())?,
+ _stdin: stdin::Handler::new(input, event_w),
+ })
+ }
+
+ pub fn new_dir(&self, path: std::path::PathBuf) {
+ self.git.new_dir(path);
+ }
+}
diff --git a/src/shell/inputs/signals.rs b/src/shell/inputs/signals.rs
new file mode 100644
index 0000000..4b91273
--- /dev/null
+++ b/src/shell/inputs/signals.rs
@@ -0,0 +1,30 @@
+use crate::shell::prelude::*;
+
+pub struct Handler;
+
+impl Handler {
+ pub fn new(event_w: crate::shell::event::Writer) -> Result<Self> {
+ let signals = tokio::signal::unix::signal(
+ tokio::signal::unix::SignalKind::window_change(),
+ )?;
+ tokio::spawn(Self::task(signals, event_w));
+ Ok(Self)
+ }
+
+ async fn task(
+ mut signals: tokio::signal::unix::Signal,
+ event_w: crate::shell::event::Writer,
+ ) {
+ event_w.send(resize_event());
+ while signals.recv().await.is_some() {
+ event_w.send(resize_event());
+ }
+ }
+}
+
+fn resize_event() -> Event {
+ Event::Resize(terminal_size::terminal_size().map_or(
+ (24, 80),
+ |(terminal_size::Width(w), terminal_size::Height(h))| (h, w),
+ ))
+}
diff --git a/src/shell/inputs/stdin.rs b/src/shell/inputs/stdin.rs
new file mode 100644
index 0000000..b966307
--- /dev/null
+++ b/src/shell/inputs/stdin.rs
@@ -0,0 +1,17 @@
+use crate::shell::prelude::*;
+
+pub struct Handler;
+
+impl Handler {
+ pub fn new(
+ mut input: textmode::blocking::Input,
+ event_w: crate::shell::event::Writer,
+ ) -> Self {
+ std::thread::spawn(move || {
+ while let Some(key) = input.read_key().unwrap() {
+ event_w.send(Event::Key(key));
+ }
+ });
+ Self
+ }
+}
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index f7080a4..fa7147b 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -1,16 +1,16 @@
use crate::shell::prelude::*;
-use notify::Watcher as _;
use textmode::Textmode as _;
mod event;
-mod git;
mod history;
+mod inputs;
+mod old_history;
mod prelude;
mod readline;
-pub async fn main() -> anyhow::Result<i32> {
- let mut input = textmode::Input::new().await?;
+pub async fn main() -> Result<i32> {
+ let mut input = textmode::blocking::Input::new()?;
let mut output = textmode::Output::new().await?;
// avoid the guards getting stuck in a task that doesn't run to
@@ -18,163 +18,40 @@ pub async fn main() -> anyhow::Result<i32> {
let _input_guard = input.take_raw_guard();
let _output_guard = output.take_screen_guard();
- let (event_w, event_r) = async_std::channel::unbounded();
+ let (event_w, event_r) = event::channel();
- {
- // nix::sys::signal::Signal is repr(i32)
- #[allow(clippy::as_conversions)]
- let signals = signal_hook_async_std::Signals::new(&[
- nix::sys::signal::Signal::SIGWINCH as i32,
- ])?;
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- // nix::sys::signal::Signal is repr(i32)
- #[allow(clippy::as_conversions)]
- let mut signals = async_std::stream::once(
- nix::sys::signal::Signal::SIGWINCH as i32,
- )
- .chain(signals);
- while signals.next().await.is_some() {
- event_w
- .send(Event::Resize(
- terminal_size::terminal_size().map_or(
- (24, 80),
- |(
- terminal_size::Width(w),
- terminal_size::Height(h),
- )| { (h, w) },
- ),
- ))
- .await
- .unwrap();
- }
- });
- }
-
- {
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- while let Some(key) = input.read_key().await.unwrap() {
- event_w.send(Event::Key(key)).await.unwrap();
- }
- });
- }
-
- // redraw the clock every second
- {
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- let first_sleep = 1_000_000_000_u64.saturating_sub(
- time::OffsetDateTime::now_utc().nanosecond().into(),
- );
- async_std::task::sleep(std::time::Duration::from_nanos(
- first_sleep,
- ))
- .await;
- let mut interval = async_std::stream::interval(
- std::time::Duration::from_secs(1),
- );
- event_w.send(Event::ClockTimer).await.unwrap();
- while interval.next().await.is_some() {
- event_w.send(Event::ClockTimer).await.unwrap();
- }
- });
- }
-
- let (git_w, git_r): (async_std::channel::Sender<std::path::PathBuf>, _) =
- async_std::channel::unbounded();
- {
- let event_w = event_w.clone();
- let mut _active_watcher = None;
- async_std::task::spawn(async move {
- while let Ok(mut dir) = git_r.recv().await {
- while let Ok(newer_dir) = git_r.try_recv() {
- dir = newer_dir;
- }
- let repo = git2::Repository::discover(&dir).ok();
- if repo.is_some() {
- let (sync_watch_w, sync_watch_r) =
- std::sync::mpsc::channel();
- let (watch_w, watch_r) = async_std::channel::unbounded();
- let mut watcher = notify::RecommendedWatcher::new(
- sync_watch_w,
- std::time::Duration::from_millis(100),
- )
- .unwrap();
- watcher
- .watch(&dir, notify::RecursiveMode::Recursive)
- .unwrap();
- async_std::task::spawn(blocking::unblock(move || {
- while let Ok(event) = sync_watch_r.recv() {
- let watch_w = watch_w.clone();
- let send_failed =
- async_std::task::block_on(async move {
- watch_w.send(event).await.is_err()
- });
- if send_failed {
- break;
- }
- }
- }));
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- while watch_r.recv().await.is_ok() {
- let repo = git2::Repository::discover(&dir).ok();
- let info = blocking::unblock(|| {
- repo.map(|repo| git::Info::new(&repo))
- })
- .await;
- if event_w
- .send(Event::GitInfo(info))
- .await
- .is_err()
- {
- break;
- }
- }
- });
- _active_watcher = Some(watcher);
- } else {
- _active_watcher = None;
- }
- let info = blocking::unblock(|| {
- repo.map(|repo| git::Info::new(&repo))
- })
- .await;
- event_w.send(Event::GitInfo(info)).await.unwrap();
- }
- });
- }
+ let inputs = inputs::Handler::new(input, event_w.clone()).unwrap();
let mut shell = Shell::new(crate::info::get_offset())?;
let mut prev_dir = shell.env.pwd().to_path_buf();
- git_w.send(prev_dir.clone()).await.unwrap();
- let event_reader = event::Reader::new(event_r);
- while let Some(event) = event_reader.recv().await {
- let dir = shell.env().pwd();
- if dir != prev_dir {
- prev_dir = dir.to_path_buf();
- git_w.send(dir.to_path_buf()).await.unwrap();
- }
- match shell.handle_event(event, &event_w).await {
+ inputs.new_dir(prev_dir.clone());
+ while let Some(event) = event_r.recv().await {
+ match shell.handle_event(event, &event_w) {
Some(Action::Refresh) => {
- shell.render(&mut output).await?;
+ shell.render(&mut output)?;
output.refresh().await?;
}
Some(Action::HardRefresh) => {
- shell.render(&mut output).await?;
+ shell.render(&mut output)?;
output.hard_refresh().await?;
}
Some(Action::Resize(rows, cols)) => {
output.set_size(rows, cols);
- shell.render(&mut output).await?;
+ shell.render(&mut output)?;
output.hard_refresh().await?;
}
Some(Action::Quit) => break,
None => {}
}
+ let dir = shell.env().pwd();
+ if dir != prev_dir {
+ prev_dir = dir.to_path_buf();
+ inputs.new_dir(dir.to_path_buf());
+ }
}
+ shell.history.save().await;
+
Ok(0)
}
@@ -201,8 +78,9 @@ pub enum Action {
pub struct Shell {
readline: readline::Readline,
history: history::History,
+ old_history: old_history::History,
env: Env,
- git: Option<git::Info>,
+ git: Option<inputs::GitInfo>,
focus: Focus,
scene: Scene,
escape: bool,
@@ -211,13 +89,14 @@ pub struct Shell {
}
impl Shell {
- pub fn new(offset: time::UtcOffset) -> anyhow::Result<Self> {
+ pub fn new(offset: time::UtcOffset) -> Result<Self> {
let mut env = Env::new()?;
env.set_var("SHELL", std::env::current_exe()?);
env.set_var("TERM", "screen");
Ok(Self {
readline: readline::Readline::new(),
history: history::History::new(),
+ old_history: old_history::History::new(),
env,
git: None,
focus: Focus::Readline,
@@ -228,87 +107,76 @@ impl Shell {
})
}
- pub async fn render(
- &self,
- out: &mut impl textmode::Textmode,
- ) -> anyhow::Result<()> {
+ pub fn render(&self, out: &mut impl textmode::Textmode) -> Result<()> {
out.clear();
out.write(&vt100::Parser::default().screen().input_mode_formatted());
match self.scene {
Scene::Readline => match self.focus {
Focus::Readline => {
- self.history
- .render(
+ self.history.render(
+ out,
+ self.readline.lines(),
+ None,
+ false,
+ self.offset,
+ );
+ self.readline.render(
+ out,
+ &self.env,
+ self.git.as_ref(),
+ true,
+ self.offset,
+ )?;
+ }
+ Focus::History(idx) => {
+ if self.hide_readline {
+ self.history.render(
+ out,
+ 0,
+ Some(idx),
+ false,
+ self.offset,
+ );
+ } else {
+ self.history.render(
out,
self.readline.lines(),
- None,
+ Some(idx),
false,
self.offset,
- )
- .await?;
- self.readline
- .render(
+ );
+ let pos = out.screen().cursor_position();
+ self.readline.render(
out,
&self.env,
self.git.as_ref(),
- true,
+ false,
self.offset,
- )
- .await?;
- }
- Focus::History(idx) => {
- if self.hide_readline {
- self.history
- .render(out, 0, Some(idx), false, self.offset)
- .await?;
- } else {
- self.history
- .render(
- out,
- self.readline.lines(),
- Some(idx),
- false,
- self.offset,
- )
- .await?;
- let pos = out.screen().cursor_position();
- self.readline
- .render(
- out,
- &self.env,
- self.git.as_ref(),
- false,
- self.offset,
- )
- .await?;
+ )?;
out.move_to(pos.0, pos.1);
}
}
Focus::Scrolling(idx) => {
- self.history
- .render(
- out,
- self.readline.lines(),
- idx,
- true,
- self.offset,
- )
- .await?;
- self.readline
- .render(
- out,
- &self.env,
- self.git.as_ref(),
- idx.is_none(),
- self.offset,
- )
- .await?;
+ self.history.render(
+ out,
+ self.readline.lines(),
+ idx,
+ true,
+ self.offset,
+ );
+ self.readline.render(
+ out,
+ &self.env,
+ self.git.as_ref(),
+ idx.is_none(),
+ self.offset,
+ )?;
out.hide_cursor(true);
}
},
Scene::Fullscreen => {
if let Focus::History(idx) = self.focus {
- self.history.render_fullscreen(out, idx).await;
+ self.history.entry(idx).render_fullscreen(out);
} else {
unreachable!();
}
@@ -317,79 +185,72 @@ impl Shell {
Ok(())
}
- pub async fn handle_event(
+ pub fn handle_event(
&mut self,
event: Event,
- event_w: &async_std::channel::Sender<Event>,
+ event_w: &crate::shell::event::Writer,
) -> Option<Action> {
match event {
Event::Key(key) => {
return if self.escape {
self.escape = false;
- self.handle_key_escape(key, event_w.clone()).await
+ self.handle_key_escape(&key, event_w.clone())
} else if key == textmode::Key::Ctrl(b'e') {
self.escape = true;
None
} else {
match self.focus {
Focus::Readline => {
- self.handle_key_readline(key, event_w.clone())
- .await
+ self.handle_key_readline(&key, event_w.clone())
}
Focus::History(idx) => {
- self.handle_key_history(key, idx).await;
+ self.handle_key_history(key, idx);
None
}
Focus::Scrolling(_) => {
- self.handle_key_escape(key, event_w.clone()).await
+ self.handle_key_escape(&key, event_w.clone())
}
}
};
}
Event::Resize(new_size) => {
- self.readline.resize(new_size).await;
- self.history.resize(new_size).await;
+ self.readline.resize(new_size);
+ self.history.resize(new_size);
return Some(Action::Resize(new_size.0, new_size.1));
}
Event::PtyOutput => {
// the number of visible lines may have changed, so make sure
// the focus is still visible
- self.history
- .make_focus_visible(
- self.readline.lines(),
- self.focus_idx(),
- matches!(self.focus, Focus::Scrolling(_)),
- )
- .await;
- self.scene = self.default_scene(self.focus, None).await;
- }
- Event::PtyClose => {
- if let Some(idx) = self.focus_idx() {
- let entry = self.history.entry(idx).await;
- if !entry.running() {
+ self.history.make_focus_visible(
+ self.readline.lines(),
+ self.focus_idx(),
+ matches!(self.focus, Focus::Scrolling(_)),
+ );
+ self.scene = self.default_scene(self.focus);
+ }
+ Event::ChildExit(idx, exit_info, env) => {
+ self.history.entry_mut(idx).exited(exit_info);
+ if self.focus_idx() == Some(idx) {
+ if let Some(env) = env {
if self.hide_readline {
let idx = self.env.idx();
- self.env = entry.env().clone();
+ self.env = env;
self.env.set_idx(idx);
}
- self.set_focus(
- if self.hide_readline {
- Focus::Readline
- } else {
- Focus::Scrolling(Some(idx))
- },
- Some(entry),
- )
- .await;
}
+ self.set_focus(if self.hide_readline {
+ Focus::Readline
+ } else {
+ Focus::Scrolling(Some(idx))
+ });
}
}
Event::ChildRunPipeline(idx, span) => {
- self.history.entry(idx).await.set_span(span);
+ self.history.entry_mut(idx).set_span(span);
}
Event::ChildSuspend(idx) => {
if self.focus_idx() == Some(idx) {
- self.set_focus(Focus::Readline, None).await;
+ self.set_focus(Focus::Readline);
}
}
Event::GitInfo(info) => {
@@ -400,18 +261,17 @@ impl Shell {
Some(Action::Refresh)
}
- async fn handle_key_escape(
+ fn handle_key_escape(
&mut self,
- key: textmode::Key,
- event_w: async_std::channel::Sender<Event>,
+ key: &textmode::Key,
+ event_w: crate::shell::event::Writer,
) -> Option<Action> {
match key {
textmode::Key::Ctrl(b'd') => {
return Some(Action::Quit);
}
textmode::Key::Ctrl(b'e') => {
- self.set_focus(Focus::Scrolling(self.focus_idx()), None)
- .await;
+ self.set_focus(Focus::Scrolling(self.focus_idx()));
}
textmode::Key::Ctrl(b'l') => {
return Some(Action::HardRefresh);
@@ -419,48 +279,37 @@ impl Shell {
textmode::Key::Ctrl(b'm') => {
if let Some(idx) = self.focus_idx() {
self.readline.clear_input();
- let entry = self.history.entry(idx).await;
- let input = entry.cmd();
- let idx = self
- .history
- .run(input, &self.env, event_w.clone())
- .await
- .unwrap();
- self.set_focus(Focus::History(idx), Some(entry)).await;
+ self.history.run(
+ self.history.entry(idx).cmd().to_string(),
+ self.env.clone(),
+ event_w,
+ );
+ let idx = self.history.entry_count() - 1;
+ self.set_focus(Focus::History(idx));
self.hide_readline = true;
self.env.set_idx(idx + 1);
} else {
- self.set_focus(Focus::Readline, None).await;
+ self.set_focus(Focus::Readline);
}
}
textmode::Key::Char(' ') => {
- let idx = self.focus_idx();
- let (focus, entry) = if let Some(idx) = idx {
- let entry = self.history.entry(idx).await;
- (entry.running(), Some(entry))
+ if let Some(idx) = self.focus_idx() {
+ if self.history.entry(idx).running() {
+ self.set_focus(Focus::History(idx));
+ }
} else {
- (true, None)
- };
- if focus {
- self.set_focus(
- idx.map_or(Focus::Readline, |idx| {
- Focus::History(idx)
- }),
- entry,
- )
- .await;
+ self.set_focus(Focus::Readline);
}
}
textmode::Key::Char('e') => {
if let Focus::History(idx) = self.focus {
- self.handle_key_history(textmode::Key::Ctrl(b'e'), idx)
- .await;
+ self.handle_key_history(textmode::Key::Ctrl(b'e'), idx);
}
}
textmode::Key::Char('f') => {
if let Some(idx) = self.focus_idx() {
- let mut entry = self.history.entry(idx).await;
let mut focus = Focus::History(idx);
+ let entry = self.history.entry_mut(idx);
if let Focus::Scrolling(_) = self.focus {
entry.set_fullscreen(true);
} else {
@@ -469,38 +318,30 @@ impl Shell {
focus = Focus::Scrolling(Some(idx));
}
}
- self.set_focus(focus, Some(entry)).await;
+ self.set_focus(focus);
}
}
textmode::Key::Char('i') => {
if let Some(idx) = self.focus_idx() {
- let entry = self.history.entry(idx).await;
- self.readline.set_input(entry.cmd());
- self.set_focus(Focus::Readline, Some(entry)).await;
+ self.readline
+ .set_input(self.history.entry(idx).cmd().to_string());
+ self.set_focus(Focus::Readline);
}
}
textmode::Key::Char('j') | textmode::Key::Down => {
- self.set_focus(
- Focus::Scrolling(self.scroll_down(self.focus_idx())),
- None,
- )
- .await;
+ self.set_focus(Focus::Scrolling(self.scroll_down()));
}
textmode::Key::Char('k') | textmode::Key::Up => {
- self.set_focus(
- Focus::Scrolling(self.scroll_up(self.focus_idx())),
- None,
- )
- .await;
+ self.set_focus(Focus::Scrolling(self.scroll_up()));
}
textmode::Key::Char('n') => {
- self.set_focus(self.next_running().await, None).await;
+ self.set_focus(self.next_running());
}
textmode::Key::Char('p') => {
- self.set_focus(self.prev_running().await, None).await;
+ self.set_focus(self.prev_running());
}
textmode::Key::Char('r') => {
- self.set_focus(Focus::Readline, None).await;
+ self.set_focus(Focus::Readline);
}
_ => {
return None;
@@ -509,10 +350,10 @@ impl Shell {
Some(Action::Refresh)
}
- async fn handle_key_readline(
+ fn handle_key_readline(
&mut self,
- key: textmode::Key,
- event_w: async_std::channel::Sender<Event>,
+ key: &textmode::Key,
+ event_w: crate::shell::event::Writer,
) -> Option<Action> {
match key {
textmode::Key::Char(c) => {
@@ -528,12 +369,13 @@ impl Shell {
textmode::Key::Ctrl(b'm') => {
let input = self.readline.input();
if !input.is_empty() {
- let idx = self
- .history
- .run(input, &self.env, event_w.clone())
- .await
- .unwrap();
- self.set_focus(Focus::History(idx), None).await;
+ self.history.run(
+ input.to_string(),
+ self.env.clone(),
+ event_w,
+ );
+ let idx = self.history.entry_count() - 1;
+ self.set_focus(Focus::History(idx));
self.hide_readline = true;
self.env.set_idx(idx + 1);
self.readline.clear_input();
@@ -546,11 +388,7 @@ impl Shell {
textmode::Key::Up => {
let entry_count = self.history.entry_count();
if entry_count > 0 {
- self.set_focus(
- Focus::Scrolling(Some(entry_count - 1)),
- None,
- )
- .await;
+ self.set_focus(Focus::Scrolling(Some(entry_count - 1)));
}
}
_ => return None,
@@ -558,24 +396,15 @@ impl Shell {
Some(Action::Refresh)
}
- async fn handle_key_history(&mut self, key: textmode::Key, idx: usize) {
- self.history.send_input(idx, key.into_bytes()).await;
+ fn handle_key_history(&mut self, key: textmode::Key, idx: usize) {
+ self.history.entry(idx).input(key.into_bytes());
}
- async fn default_scene(
- &self,
- focus: Focus,
- entry: Option<crate::mutex::Guard<history::Entry>>,
- ) -> Scene {
+ fn default_scene(&self, focus: Focus) -> Scene {
match focus {
Focus::Readline | Focus::Scrolling(_) => Scene::Readline,
Focus::History(idx) => {
- let fullscreen = if let Some(entry) = entry {
- entry.should_fullscreen()
- } else {
- self.history.entry(idx).await.should_fullscreen()
- };
- if fullscreen {
+ if self.history.entry(idx).should_fullscreen() {
Scene::Fullscreen
} else {
Scene::Readline
@@ -584,25 +413,15 @@ impl Shell {
}
}
- async fn set_focus(
- &mut self,
- new_focus: Focus,
- entry: Option<crate::mutex::Guard<history::Entry>>,
- ) {
+ fn set_focus(&mut self, new_focus: Focus) {
self.focus = new_focus;
self.hide_readline = false;
- self.scene = self.default_scene(new_focus, entry).await;
- // passing entry into default_scene above consumes it, which means
- // that the mutex lock will be dropped before we call into
- // make_focus_visible, which is important because otherwise we might
- // get a deadlock depending on what is visible
- self.history
- .make_focus_visible(
- self.readline.lines(),
- self.focus_idx(),
- matches!(self.focus, Focus::Scrolling(_)),
- )
- .await;
+ self.scene = self.default_scene(new_focus);
+ self.history.make_focus_visible(
+ self.readline.lines(),
+ self.focus_idx(),
+ matches!(self.focus, Focus::Scrolling(_)),
+ );
}
fn env(&self) -> &Env {
@@ -617,8 +436,8 @@ impl Shell {
}
}
- fn scroll_up(&self, idx: Option<usize>) -> Option<usize> {
- idx.map_or_else(
+ fn scroll_up(&self) -> Option<usize> {
+ self.focus_idx().map_or_else(
|| {
let count = self.history.entry_count();
if count == 0 {
@@ -631,8 +450,8 @@ impl Shell {
)
}
- fn scroll_down(&self, idx: Option<usize>) -> Option<usize> {
- idx.and_then(|idx| {
+ fn scroll_down(&self) -> Option<usize> {
+ self.focus_idx().and_then(|idx| {
if idx >= self.history.entry_count() - 1 {
None
} else {
@@ -641,25 +460,25 @@ impl Shell {
})
}
- async fn next_running(&self) -> Focus {
+ fn next_running(&self) -> Focus {
let count = self.history.entry_count();
let cur = self.focus_idx().unwrap_or(count);
for idx in ((cur + 1)..count).chain(0..cur) {
- if self.history.entry(idx).await.running() {
+ if self.history.entry(idx).running() {
return Focus::History(idx);
}
}
- self.focus_idx().map_or(Focus::Readline, Focus::History)
+ self.focus
}
- async fn prev_running(&self) -> Focus {
+ fn prev_running(&self) -> Focus {
let count = self.history.entry_count();
let cur = self.focus_idx().unwrap_or(count);
for idx in ((cur + 1)..count).chain(0..cur).rev() {
- if self.history.entry(idx).await.running() {
+ if self.history.entry(idx).running() {
return Focus::History(idx);
}
}
- self.focus_idx().map_or(Focus::Readline, Focus::History)
+ self.focus
}
}
diff --git a/src/shell/old_history.rs b/src/shell/old_history.rs
new file mode 100644
index 0000000..49fd1c2
--- /dev/null
+++ b/src/shell/old_history.rs
@@ -0,0 +1,185 @@
+use crate::shell::prelude::*;
+
+use tokio::io::AsyncBufReadExt as _;
+
+use pest::Parser as _;
+
+#[derive(pest_derive::Parser)]
+#[grammar = "history.pest"]
+struct HistoryLine;
+
+pub struct History {
+ entries: std::sync::Arc<std::sync::Mutex<Vec<Entry>>>,
+}
+
+impl History {
+ pub fn new() -> Self {
+ let entries = std::sync::Arc::new(std::sync::Mutex::new(vec![]));
+ tokio::spawn(Self::task(std::sync::Arc::clone(&entries)));
+ Self { entries }
+ }
+
+ pub fn entry_count(&self) -> usize {
+ self.entries.lock().unwrap().len()
+ }
+
+ async fn task(entries: std::sync::Arc<std::sync::Mutex<Vec<Entry>>>) {
+ // TODO: we should actually read this in reverse order, because we
+ // want to populate the most recent entries first
+ let mut stream = tokio_stream::wrappers::LinesStream::new(
+ tokio::io::BufReader::new(
+ tokio::fs::File::open(crate::dirs::history_file())
+ .await
+ .unwrap(),
+ )
+ .lines(),
+ );
+ while let Some(line) = stream.next().await {
+ let line = if let Ok(line) = line {
+ line
+ } else {
+ continue;
+ };
+ let entry = if let Ok(entry) = line.parse() {
+ entry
+ } else {
+ continue;
+ };
+ entries.lock().unwrap().push(entry);
+ }
+ }
+}
+
+pub struct Entry {
+ cmdline: String,
+ start_time: Option<time::OffsetDateTime>,
+ duration: Option<std::time::Duration>,
+}
+
+impl Entry {
+ pub fn render(
+ &self,
+ out: &mut impl textmode::Textmode,
+ offset: time::UtcOffset,
+ ) {
+ let size = out.screen().size();
+ let mut time = "".to_string();
+ if let Some(duration) = self.duration {
+ time.push_str(&crate::format::duration(duration));
+ }
+ if let Some(start_time) = self.start_time {
+ time.push_str(&crate::format::time(start_time.to_offset(offset)));
+ }
+
+ out.write_str(" $ ");
+ let start = usize::from(out.screen().cursor_position().1);
+ let end = usize::from(size.1) - time.len() - 2;
+ let max_len = end - start;
+ let cmd = if self.cmdline.len() > max_len {
+ &self.cmdline[..(max_len - 4)]
+ } else {
+ &self.cmdline
+ };
+ out.write_str(cmd);
+ if self.cmdline.len() > max_len {
+ out.write_str(" ");
+ out.set_fgcolor(textmode::color::BLUE);
+ out.write_str("...");
+ }
+ out.reset_attributes();
+
+ out.set_bgcolor(textmode::Color::Rgb(0x20, 0x20, 0x20));
+ let cur_pos = out.screen().cursor_position();
+ out.write_str(&" ".repeat(
+ usize::from(size.1) - time.len() - 1 - usize::from(cur_pos.1),
+ ));
+ out.write_str(&time);
+ out.write_str(" ");
+ out.reset_attributes();
+ }
+
+ pub fn cmd(&self) -> &str {
+ &self.cmdline
+ }
+}
+
+impl std::str::FromStr for Entry {
+ type Err = anyhow::Error;
+
+ fn from_str(line: &str) -> std::result::Result<Self, Self::Err> {
+ let mut parsed =
+ HistoryLine::parse(Rule::line, line).map_err(|e| anyhow!(e))?;
+ let line = parsed.next().unwrap();
+ assert!(matches!(line.as_rule(), Rule::line));
+
+ let mut start_time = None;
+ let mut duration = None;
+ let mut cmdline = None;
+ for part in line.into_inner() {
+ match part.as_rule() {
+ Rule::time => {
+ start_time =
+ Some(time::OffsetDateTime::from_unix_timestamp(
+ part.as_str().parse()?,
+ )?);
+ }
+ Rule::duration => {
+ if part.as_str() == "0" {
+ continue;
+ }
+ let mut dur_parts = part.as_str().split('.');
+ let secs: u64 = dur_parts.next().unwrap().parse()?;
+ let nsec_str = dur_parts.next().unwrap_or("0");
+ let nsec_str = &nsec_str[..9.min(nsec_str.len())];
+ let nsecs: u64 = nsec_str.parse()?;
+ duration = Some(std::time::Duration::from_nanos(
+ secs * 1_000_000_000
+ + nsecs
+ * (10u64.pow(
+ (9 - nsec_str.len()).try_into().unwrap(),
+ )),
+ ));
+ }
+ Rule::command => {
+ cmdline = Some(part.as_str().to_string());
+ }
+ Rule::line => unreachable!(),
+ Rule::EOI => break,
+ }
+ }
+
+ Ok(Self {
+ cmdline: cmdline.unwrap(),
+ start_time,
+ duration,
+ })
+ }
+}
+
+#[test]
+fn test_parse() {
+ let entry: Entry =
+ ": 1646779848:1234.56;vim ~/.zsh_history".parse().unwrap();
+ assert_eq!(entry.cmdline, "vim ~/.zsh_history");
+ assert_eq!(
+ entry.duration,
+ Some(std::time::Duration::from_nanos(1_234_560_000_000))
+ );
+ assert_eq!(
+ entry.start_time,
+ Some(time::macros::datetime!(2022-03-08 22:50:48).assume_utc())
+ );
+
+ let entry: Entry = ": 1646779848:1;vim ~/.zsh_history".parse().unwrap();
+ assert_eq!(entry.cmdline, "vim ~/.zsh_history");
+ assert_eq!(entry.duration, Some(std::time::Duration::from_secs(1)));
+ assert_eq!(
+ entry.start_time,
+ Some(time::macros::datetime!(2022-03-08 22:50:48).assume_utc())
+ );
+
+ let entry: Entry = "vim ~/.zsh_history".parse().unwrap();
+ assert_eq!(entry.cmdline, "vim ~/.zsh_history");
+ assert_eq!(entry.duration, None);
+ assert_eq!(entry.start_time, None);
+}
diff --git a/src/shell/readline.rs b/src/shell/readline.rs
index f0fb950..654d264 100644
--- a/src/shell/readline.rs
+++ b/src/shell/readline.rs
@@ -19,14 +19,14 @@ impl Readline {
}
}
- pub async fn render(
+ pub fn render(
&self,
out: &mut impl textmode::Textmode,
env: &Env,
- git: Option<&super::git::Info>,
+ git: Option<&super::inputs::GitInfo>,
focus: bool,
offset: time::UtcOffset,
- ) -> anyhow::Result<()> {
+ ) -> Result<()> {
let pwd = env.pwd();
let user = crate::info::user()?;
let hostname = crate::info::hostname()?;
@@ -83,7 +83,7 @@ impl Readline {
Ok(())
}
- pub async fn resize(&mut self, size: (u16, u16)) {
+ pub fn resize(&mut self, size: (u16, u16)) {
self.size = size;
}
@@ -102,9 +102,9 @@ impl Readline {
self.inc_pos(s.chars().count());
}
- pub fn set_input(&mut self, s: &str) {
- self.input_line = s.to_string();
+ pub fn set_input(&mut self, s: String) {
self.set_pos(s.chars().count());
+ self.input_line = s;
}
pub fn backspace(&mut self) {