Refactor using async-std

This commit is contained in:
Dheepak Krishnamurthy 2021-03-30 18:48:15 -06:00
parent 6df2aba770
commit 979e886a29
7 changed files with 919 additions and 162 deletions

View file

@ -41,7 +41,7 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
args: --all -- --nocapture
args: --all -- --test-threads=1 --nocapture
fmt:
name: Rustfmt

582
Cargo.lock generated
View file

@ -41,6 +41,152 @@ version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "async-attributes"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "async-channel"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-executor"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"once_cell",
"vec-arena",
]
[[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.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd"
dependencies = [
"concurrent-queue",
"fastrand",
"futures-lite",
"libc",
"log",
"nb-connect",
"once_cell",
"parking",
"polling",
"vec-arena",
"waker-fn",
"winapi",
]
[[package]]
name = "async-lock"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb"
dependencies = [
"event-listener",
]
[[package]]
name = "async-mutex"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
dependencies = [
"event-listener",
]
[[package]]
name = "async-process"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef37b86e2fa961bae5a4d212708ea0154f904ce31d1a4a7f47e1bbc33a0c040b"
dependencies = [
"async-io",
"blocking",
"cfg-if 1.0.0",
"event-listener",
"futures-lite",
"once_cell",
"signal-hook 0.3.7",
"winapi",
]
[[package]]
name = "async-std"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341"
dependencies = [
"async-attributes",
"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",
]
[[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"
@ -88,6 +234,32 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blocking"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
dependencies = [
"async-channel",
"async-task",
"atomic-waker",
"fastrand",
"futures-lite",
"once_cell",
]
[[package]]
name = "bumpalo"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
[[package]]
name = "cache-padded"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "cassowary"
version = "0.3.0"
@ -161,6 +333,15 @@ dependencies = [
"bitflags",
]
[[package]]
name = "concurrent-queue"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [
"cache-padded",
]
[[package]]
name = "console"
version = "0.9.2"
@ -176,6 +357,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.17.7"
@ -184,11 +376,12 @@ checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7"
dependencies = [
"bitflags",
"crossterm_winapi",
"futures-util",
"lazy_static",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook 0.1.17",
"winapi",
]
@ -201,6 +394,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "ctor"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "darling"
version = "0.10.2"
@ -294,6 +497,12 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "event-listener"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "failure"
version = "0.1.8"
@ -316,6 +525,15 @@ dependencies = [
"synstructure",
]
[[package]]
name = "fastrand"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
dependencies = [
"instant",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -332,6 +550,119 @@ dependencies = [
"winapi",
]
[[package]]
name = "futures"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
[[package]]
name = "futures-executor"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
[[package]]
name = "futures-lite"
version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-macro"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
[[package]]
name = "futures-task"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
[[package]]
name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]]
name = "futures-util"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -360,6 +691,19 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
[[package]]
name = "gloo-timers"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "hermit-abi"
version = "0.1.18"
@ -375,6 +719,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "itertools"
version = "0.9.0"
@ -390,6 +743,24 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -418,6 +789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
"value-bag",
]
[[package]]
@ -458,6 +830,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "nb-connect"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d"
dependencies = [
"libc",
"socket2",
]
[[package]]
name = "nix"
version = "0.19.1"
@ -498,12 +880,34 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
[[package]]
name = "once_cell"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
version = "0.10.2"
@ -528,12 +932,49 @@ dependencies = [
"winapi",
]
[[package]]
name = "pin-project-lite"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "polling"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b"
dependencies = [
"cfg-if 1.0.0",
"libc",
"log",
"wepoll-sys",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.24"
@ -731,6 +1172,16 @@ dependencies = [
"signal-hook-registry",
]
[[package]]
name = "signal-hook"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
@ -740,12 +1191,28 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -760,9 +1227,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
version = "1.0.64"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702"
dependencies = [
"proc-macro2",
"quote",
@ -801,11 +1268,14 @@ name = "taskwarrior-tui"
version = "0.12.2"
dependencies = [
"anyhow",
"async-std",
"better-panic",
"cassowary",
"chrono",
"clap",
"crossterm",
"futures",
"futures-timer",
"itertools",
"rand",
"regex",
@ -896,12 +1366,33 @@ dependencies = [
"serde",
]
[[package]]
name = "value-bag"
version = "1.0.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b676010e055c99033117c2343b33a40a30b91fecd6c49055ac9cd2d6c305ab1"
dependencies = [
"ctor",
]
[[package]]
name = "vec-arena"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@ -914,6 +1405,91 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
[[package]]
name = "web-sys"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wepoll-sys"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
dependencies = [
"cc",
]
[[package]]
name = "winapi"
version = "0.3.9"

View file

@ -31,12 +31,15 @@ chrono = "0.4"
unicode-width = "0.1"
unicode-segmentation = "1.6"
tui = { version = "0.12", optional = true, default-features = false }
crossterm = { version = "0.17", optional = true, default-features = false }
crossterm = { version = "0.17", optional = true, default-features = false, features = ["event-stream"] }
rustyline = "7.1.0"
uuid = { version = "0.8.1", features = ["serde", "v4"] }
better-panic = "0.2.0"
shellexpand = "2.1"
anyhow = "1"
async-std = { version = "1", features = ["attributes", "unstable"] }
futures = "0.3"
futures-timer = "3.0"
[package.metadata.rpm]
package = "taskwarrior-tui"
@ -46,3 +49,6 @@ buildflags = ["--release"]
[package.metadata.rpm.targets]
taskwarrior-tui = { path = "/usr/bin/taskwarrior-tui" }
[profile.release]
debug = true

View file

@ -30,7 +30,16 @@ use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, TimeZone};
use anyhow::Result;
use std::{sync::mpsc, thread, time::Duration};
use async_std::prelude::*;
use async_std::stream::StreamExt;
use async_std::task;
use futures::future::join_all;
use futures::join;
use futures::stream::FuturesOrdered;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tui::{
backend::Backend,
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
@ -140,6 +149,7 @@ pub enum AppMode {
pub struct TaskwarriorTuiApp {
pub should_quit: bool,
pub dirty: bool,
pub task_table_state: TableState,
pub context_table_state: TableState,
pub current_context_filter: String,
@ -173,8 +183,10 @@ impl TaskwarriorTuiApp {
let c = Config::default()?;
let mut kc = KeyConfig::default();
kc.update()?;
let (w, h) = crossterm::terminal::size()?;
let mut app = Self {
should_quit: false,
dirty: true,
task_table_state: TableState::default(),
context_table_state: TableState::default(),
tasks: vec![],
@ -198,8 +210,8 @@ impl TaskwarriorTuiApp {
contexts: vec![],
last_export: None,
keyconfig: kc,
terminal_width: 0,
terminal_height: 0,
terminal_width: w,
terminal_height: h,
};
for c in app.config.filter.chars() {
app.filter.insert(c, 1);
@ -223,6 +235,14 @@ impl TaskwarriorTuiApp {
Ok(())
}
pub fn render<B>(&mut self, terminal: &mut Terminal<B>) -> Result<()>
where
B: Backend,
{
terminal.draw(|f| self.draw(f))?;
Ok(())
}
pub fn draw(&mut self, f: &mut Frame<impl Backend>) {
let rect = f.size();
self.terminal_width = rect.width;
@ -280,7 +300,6 @@ impl TaskwarriorTuiApp {
let mut tasks_with_styles = vec![];
let tasks_is_empty = self.tasks.is_empty();
let tasks_len = self.tasks.len();
if !tasks_is_empty {
let tasks = &self.tasks;
@ -305,8 +324,7 @@ impl TaskwarriorTuiApp {
pub fn draw_task(&mut self, f: &mut Frame<impl Backend>) {
let tasks_is_empty = self.tasks.is_empty();
let tasks_len = self.tasks.len();
while !tasks_is_empty && self.current_selection >= tasks_len {
while !tasks_is_empty && self.current_selection >= self.tasks.len() {
self.task_report_previous();
}
let rects = Layout::default()
@ -333,7 +351,7 @@ impl TaskwarriorTuiApp {
self.draw_task_details(f, split_task_layout[1]);
}
let selected = self.current_selection;
let task_ids = if tasks_len == 0 {
let task_ids = if self.tasks.is_empty() {
vec!["0".to_string()]
} else {
match self.task_table_state.mode() {
@ -593,24 +611,14 @@ impl TaskwarriorTuiApp {
let task_id = self.tasks[selected].id().unwrap_or_default();
let task_uuid = *self.tasks[selected].uuid();
if !self.task_details.contains_key(&task_uuid) {
let output = Command::new("task")
.arg("rc.color=off")
.arg(format!("rc.defaultwidth={}", self.terminal_width - 2))
.arg(format!("{}", task_uuid))
.output();
let data = if let Ok(output) = output {
String::from_utf8_lossy(&output.stdout).to_string()
} else {
"".to_string()
let data = match self.task_details.get(&task_uuid) {
Some(s) => s.clone(),
None => "Loading task details ...".to_string(),
};
let entry = self.task_details.entry(task_uuid).or_insert_with(|| "".to_string());
*entry = data;
}
let data = self.task_details[&task_uuid].clone();
self.task_details_scroll = std::cmp::min(
(data.lines().count() as u16).saturating_sub(rect.height),
(data.lines().count() as u16)
.saturating_sub(rect.height)
.saturating_add(2),
self.task_details_scroll,
);
let p = Paragraph::new(Text::from(&data[..]))
@ -624,11 +632,11 @@ impl TaskwarriorTuiApp {
f.render_widget(p, rect);
}
fn task_details_scroll_down(&mut self) {
fn task_details_scroll_up(&mut self) {
self.task_details_scroll = self.task_details_scroll.saturating_sub(1);
}
fn task_details_scroll_up(&mut self) {
fn task_details_scroll_down(&mut self) {
self.task_details_scroll = self.task_details_scroll.saturating_add(1);
}
@ -856,13 +864,83 @@ impl TaskwarriorTuiApp {
}
pub fn update(&mut self, force: bool) -> Result<()> {
if force || self.tasks_changed_since(self.last_export)? {
if force || self.dirty || self.tasks_changed_since(self.last_export)? {
self.last_export = Some(std::time::SystemTime::now());
self.task_report_table.export_headers()?;
let _ = self.export_tasks();
self.export_contexts()?;
self.update_tags();
self.task_details.clear();
self.dirty = false;
}
if self.task_report_show_info {
task::block_on(self.update_task_details())?;
}
Ok(())
}
pub async fn update_task_details(&mut self) -> Result<()> {
if self.tasks.is_empty() {
return Ok(());
}
// remove task_details of tasks not in task report
let mut to_delete = vec![];
for k in self.task_details.keys() {
if !self.tasks.iter().map(|t| t.uuid()).any(|x| x == k) {
to_delete.push(*k);
}
}
for k in to_delete {
self.task_details.remove(&k);
}
let selected = self.current_selection;
if selected >= self.tasks.len() {
return Ok(());
}
let current_task_uuid = *self.tasks[selected].uuid();
let mut l = vec![selected];
for s in 1..=self.config.uda_task_detail_prefetch {
l.insert(0, std::cmp::min(selected.saturating_sub(s), self.tasks.len() - 1));
l.push(std::cmp::min(selected + s, self.tasks.len() - 1))
}
l.dedup();
let mut output_futs = FuturesOrdered::new();
for s in l.iter() {
if self.tasks.is_empty() {
return Ok(());
}
if s >= &self.tasks.len() {
break;
}
let task_uuid = *self.tasks[*s].uuid();
if !self.task_details.contains_key(&task_uuid) || task_uuid == current_task_uuid {
let output_fut = async_std::process::Command::new("task")
.arg("rc.color=off")
.arg(format!("rc.defaultwidth={}", self.terminal_width - 2))
.arg(format!("{}", task_uuid))
.output();
output_futs.push(output_fut);
}
}
for s in l.iter() {
if s >= &self.tasks.len() {
break;
}
let task_id = self.tasks[*s].id().unwrap_or_default();
let task_uuid = *self.tasks[*s].uuid();
if !self.task_details.contains_key(&task_uuid) || task_uuid == current_task_uuid {
if let Some(Ok(output)) = output_futs.next().await {
let data = String::from_utf8_lossy(&output.stdout).to_string();
self.task_details.insert(task_uuid, data);
}
}
}
Ok(())
}
@ -1708,9 +1786,9 @@ impl TaskwarriorTuiApp {
} else if input == Key::PageUp || input == self.keyconfig.page_up {
self.task_report_previous_page();
} else if input == Key::Ctrl('e') {
self.task_details_scroll_up();
} else if input == Key::Ctrl('y') {
self.task_details_scroll_down();
} else if input == Key::Ctrl('y') {
self.task_details_scroll_up();
} else if input == self.keyconfig.done {
match self.task_done() {
Ok(_) => self.update(true)?,
@ -1983,7 +2061,7 @@ impl TaskwarriorTuiApp {
}
_ => {
handle_movement(&mut self.filter, input);
// TODO: call self.update(true) here for instant filter updates
self.dirty = true;
}
},
AppMode::TaskError => self.mode = AppMode::TaskReport,
@ -2123,6 +2201,9 @@ mod tests {
test_draw_empty_task_report();
test_draw_calendar();
test_draw_help_popup();
setup();
let app = TaskwarriorTuiApp::new().unwrap();
@ -2141,6 +2222,7 @@ mod tests {
test_task_tomorrow();
test_task_earlier_today();
test_task_later_today();
teardown();
}
@ -2767,7 +2849,7 @@ mod tests {
"╭Task 27─────────────────────────────────────────╮",
"│ │",
"│Name Value │",
"│----------- ------------------------------------│",
"│------------- ----------------------------------│",
"│ID 27 │",
"╰────────────────────────────────────────────────╯",
"╭Filter Tasks────────────────────────────────────╮",
@ -2824,7 +2906,6 @@ mod tests {
test_case(&expected);
}
#[test]
fn test_draw_calendar() {
let test_case = |expected: &Buffer| {
let mut app = TaskwarriorTuiApp::new().unwrap();
@ -2909,7 +2990,6 @@ mod tests {
test_case(&expected);
}
#[test]
fn test_draw_help_popup() {
let test_case = |expected: &Buffer| {
let mut app = TaskwarriorTuiApp::new().unwrap();

View file

@ -1,4 +1,6 @@
use anyhow::{Context, Result};
use async_std::task;
use futures::join;
use std::collections::HashMap;
use std::error::Error;
use std::process::Command;
@ -43,6 +45,7 @@ pub struct Config {
pub print_empty_columns: bool,
pub due: usize,
pub rule_precedence_color: Vec<String>,
pub uda_task_detail_prefetch: usize,
pub uda_task_report_show_info: bool,
pub uda_task_report_looping: bool,
pub uda_selection_indicator: String,
@ -61,28 +64,102 @@ pub struct Config {
impl Config {
pub fn default() -> Result<Self> {
let bool_collection = Self::get_bool_collection();
let enabled = true;
let obfuscate = bool_collection.get("obfuscate").cloned().unwrap_or(false);
let print_empty_columns = bool_collection.get("print_empty_columns").cloned().unwrap_or(false);
let color = Self::get_color_collection();
let filter = Self::get_filter();
let data_location = Self::get_data_location();
let due = Self::get_due();
let rule_precedence_color = Self::get_rule_precedence_color();
let uda_task_detail_prefetch = Self::get_uda_task_detail_prefetch();
let uda_task_report_show_info = Self::get_uda_task_report_show_info();
let uda_task_report_looping = Self::get_uda_task_report_looping();
let uda_selection_indicator = Self::get_uda_selection_indicator();
let uda_mark_indicator = Self::get_uda_mark_indicator();
let uda_unmark_indicator = Self::get_uda_unmark_indicator();
let uda_selection_bold = Self::get_uda_selection_bold();
let uda_selection_italic = Self::get_uda_selection_italic();
let uda_selection_dim = Self::get_uda_selection_dim();
let uda_selection_blink = Self::get_uda_selection_blink();
let uda_calendar_months_per_row = Self::get_uda_months_per_row();
let uda_style_calendar_title = Self::get_uda_style("calendar.title");
let uda_style_context_active = Self::get_uda_style("context.active");
let uda_shortcuts = Self::get_uda_shortcuts();
let (
color,
filter,
data_location,
due,
rule_precedence_color,
uda_task_detail_prefetch,
uda_task_report_show_info,
uda_task_report_looping,
uda_selection_indicator,
uda_mark_indicator,
uda_unmark_indicator,
uda_selection_bold,
uda_selection_italic,
uda_selection_dim,
uda_selection_blink,
uda_calendar_months_per_row,
uda_style_calendar_title,
uda_style_context_active,
uda_shortcuts,
) = task::block_on(async {
join!(
color,
filter,
data_location,
due,
rule_precedence_color,
uda_task_detail_prefetch,
uda_task_report_show_info,
uda_task_report_looping,
uda_selection_indicator,
uda_mark_indicator,
uda_unmark_indicator,
uda_selection_bold,
uda_selection_italic,
uda_selection_dim,
uda_selection_blink,
uda_calendar_months_per_row,
uda_style_calendar_title,
uda_style_context_active,
uda_shortcuts,
)
});
let color = color?;
let uda_style_calendar_title = uda_style_calendar_title.unwrap_or_default();
let uda_style_context_active = uda_style_context_active.unwrap_or_default();
Ok(Self {
enabled: true,
obfuscate: bool_collection.get("obfuscate").cloned().unwrap_or(false),
print_empty_columns: bool_collection.get("print_empty_columns").cloned().unwrap_or(false),
color: Self::get_color_collection()?,
filter: Self::get_filter(),
data_location: Self::get_data_location(),
due: Self::get_due(),
rule_precedence_color: Self::get_rule_precedence_color(),
uda_task_report_show_info: Self::get_uda_task_report_show_info(),
uda_task_report_looping: Self::get_uda_task_report_looping(),
uda_selection_indicator: Self::get_uda_selection_indicator(),
uda_mark_indicator: Self::get_uda_mark_indicator(),
uda_unmark_indicator: Self::get_uda_unmark_indicator(),
uda_selection_bold: Self::get_uda_selection_bold(),
uda_selection_italic: Self::get_uda_selection_italic(),
uda_selection_dim: Self::get_uda_selection_dim(),
uda_selection_blink: Self::get_uda_selection_blink(),
uda_calendar_months_per_row: Self::get_uda_months_per_row(),
uda_style_calendar_title: Self::get_uda_style("calendar.title").unwrap_or_default(),
uda_style_context_active: Self::get_uda_style("context.active").unwrap_or_default(),
uda_shortcuts: Self::get_uda_shortcuts(),
enabled,
color,
filter,
data_location,
obfuscate,
print_empty_columns,
due,
rule_precedence_color,
uda_task_detail_prefetch,
uda_task_report_show_info,
uda_task_report_looping,
uda_selection_indicator,
uda_mark_indicator,
uda_unmark_indicator,
uda_selection_bold,
uda_selection_italic,
uda_selection_dim,
uda_selection_blink,
uda_calendar_months_per_row,
uda_style_context_active,
uda_style_calendar_title,
uda_shortcuts,
})
}
@ -90,25 +167,29 @@ impl Config {
HashMap::new()
}
fn get_uda_shortcuts() -> Vec<String> {
async fn get_uda_shortcuts() -> Vec<String> {
let mut v = vec![];
for s in 0..=9 {
let c = format!("uda.taskwarrior-tui.shortcuts.{}", s);
let s = Self::get_config(&c).unwrap_or_default();
let s = Self::get_config(&c).await.unwrap_or_default();
v.push(s);
}
v
}
fn get_uda_style(config: &str) -> Option<Style> {
async fn get_uda_style(config: &str) -> Option<Style> {
let c = format!("uda.taskwarrior-tui.style.{}", config);
let s = Self::get_config(&c)?;
let s = Self::get_config(&c).await?;
Some(Self::get_tcolor(&s))
}
fn get_color_collection() -> Result<HashMap<String, Style>> {
async fn get_color_collection() -> Result<HashMap<String, Style>> {
let mut color_collection = HashMap::new();
let output = Command::new("task").arg("rc.color=off").arg("show").output()?;
let output = async_std::process::Command::new("task")
.arg("rc.color=off")
.arg("show")
.output()
.await?;
let data = String::from_utf8_lossy(&output.stdout);
for line in data.split('\n') {
@ -274,12 +355,13 @@ impl Config {
}
}
fn get_config(config: &str) -> Option<String> {
let output = Command::new("task")
async fn get_config(config: &str) -> Option<String> {
let output = async_std::process::Command::new("task")
.arg("rc.color=off")
.arg("show")
.arg(config)
.output()
.await
.with_context(|| format!("Unable to run `task show {}`.", config))
.unwrap();
@ -311,100 +393,119 @@ impl Config {
None
}
fn get_due() -> usize {
async fn get_due() -> usize {
Self::get_config("due")
.await
.unwrap_or_default()
.parse::<usize>()
.unwrap_or(7)
}
fn get_rule_precedence_color() -> Vec<String> {
async fn get_rule_precedence_color() -> Vec<String> {
let data = Self::get_config("rule.precedence.color")
.await
.context("Unable to parse `task show rule.precedence.color`.")
.unwrap();
data.split(',').map(|s| s.to_string()).collect::<Vec<_>>()
}
fn get_filter() -> String {
async fn get_filter() -> String {
Self::get_config("report.next.filter")
.await
.context("Unable to parse `task show report.next.filter`.")
.unwrap()
}
fn get_data_location() -> String {
async fn get_data_location() -> String {
Self::get_config("data.location")
.await
.context("Unable to parse `task show data.location`.")
.unwrap()
}
fn get_uda_task_report_show_info() -> bool {
async fn get_uda_task_detail_prefetch() -> usize {
Self::get_config("uda.taskwarrior-tui.task-report.task-detail-prefetch")
.await
.unwrap_or_default()
.parse::<usize>()
.unwrap_or(10)
}
async fn get_uda_task_report_show_info() -> bool {
Self::get_config("uda.taskwarrior-tui.task-report.show-info")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(true)
}
fn get_uda_task_report_looping() -> bool {
async fn get_uda_task_report_looping() -> bool {
Self::get_config("uda.taskwarrior-tui.task-report.looping")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(true)
}
fn get_uda_selection_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.selection.indicator");
async fn get_uda_selection_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.selection.indicator").await;
match indicator {
None => "".to_string(),
Some(indicator) => format!("{} ", indicator),
}
}
fn get_uda_mark_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.mark.indicator");
async fn get_uda_mark_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.mark.indicator").await;
match indicator {
None => "".to_string(),
Some(indicator) => format!("{} ", indicator),
}
}
fn get_uda_unmark_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.unmark.indicator");
async fn get_uda_unmark_indicator() -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.unmark.indicator").await;
match indicator {
None => " ".to_string(),
Some(indicator) => format!("{} ", indicator),
}
}
fn get_uda_selection_bold() -> bool {
async fn get_uda_selection_bold() -> bool {
Self::get_config("uda.taskwarrior-tui.selection.bold")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(true)
}
fn get_uda_selection_italic() -> bool {
async fn get_uda_selection_italic() -> bool {
Self::get_config("uda.taskwarrior-tui.selection.italic")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(false)
}
fn get_uda_selection_dim() -> bool {
async fn get_uda_selection_dim() -> bool {
Self::get_config("uda.taskwarrior-tui.selection.dim")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(false)
}
fn get_uda_selection_blink() -> bool {
async fn get_uda_selection_blink() -> bool {
Self::get_config("uda.taskwarrior-tui.selection.blink")
.await
.unwrap_or_default()
.get_bool()
.unwrap_or(false)
}
fn get_uda_months_per_row() -> usize {
async fn get_uda_months_per_row() -> usize {
Self::get_config("uda.taskwarrior-tui.calendar.months-per-row")
.await
.unwrap_or_default()
.parse::<usize>()
.unwrap_or(4)
@ -416,7 +517,10 @@ mod tests {
use super::*;
#[test]
fn test_uda_configuration() {
assert_eq!(None, Config::get_config("uda.taskwarrior-tui.unmark.indicator"));
assert_eq!(
None,
task::block_on(Config::get_config("uda.taskwarrior-tui.unmark.indicator"))
);
}
#[test]

View file

@ -21,6 +21,11 @@ use std::io::Write;
use std::panic;
use std::time::Duration;
use async_std::prelude::*;
use async_std::sync::{Arc, Mutex};
use async_std::task;
use futures::stream::{FuturesUnordered, StreamExt};
use crate::util::Key;
use app::{AppMode, TaskwarriorTuiApp};
@ -44,28 +49,12 @@ fn main() -> Result<()> {
.get_matches();
let config = matches.value_of("config").unwrap_or("~/.taskrc");
let r = tui_main(config);
match r {
Ok(_) => std::process::exit(0),
Err(error) => {
if error.to_string().to_lowercase().contains("no such file or directory") {
eprintln!(
"[taskwarrior-tui error]: Unable to find executable `task`: {}. Check that taskwarrior is installed correctly and try again.", error
);
} else {
eprintln!(
"[taskwarrior-tui error]: {}. Please report as a github issue on https://github.com/kdheepak/taskwarrior-tui",
error
);
}
std::process::exit(1);
}
}
task::block_on(tui_main(config))
}
fn tui_main(_config: &str) -> Result<()> {
async fn tui_main(_config: &str) -> Result<()> {
// Terminal initialization
let mut terminal = setup_terminal();
let terminal = setup_terminal();
panic::set_hook(Box::new(|panic_info| {
destruct_terminal();
@ -79,21 +68,30 @@ fn tui_main(_config: &str) -> Result<()> {
let maybeapp = TaskwarriorTuiApp::new();
match maybeapp {
Ok(mut app) => {
Ok(app) => {
let app = Arc::new(Mutex::new(app));
let terminal = Arc::new(Mutex::new(terminal));
loop {
terminal.draw(|mut frame| app.draw(&mut frame)).unwrap();
let handle = {
let app = app.clone();
let terminal = terminal.clone();
task::spawn_local(async move {
let mut t = terminal.lock().await;
app.lock().await.render(&mut t).unwrap();
})
};
// Handle input
match events.next()? {
match events.next().await? {
Event::Input(input) => {
let r = app.handle_input(input, &mut terminal, &events);
let mut t = terminal.lock().await;
let r = app.lock().await.handle_input(input, &mut t, &events);
if r.is_err() {
destruct_terminal();
return r;
}
}
Event::Tick => {
let r = app.update(false);
let r = app.lock().await.update(false);
if r.is_err() {
destruct_terminal();
return r;
@ -101,7 +99,7 @@ fn tui_main(_config: &str) -> Result<()> {
}
}
if app.should_quit {
if app.lock().await.should_quit {
destruct_terminal();
break;
}

View file

@ -1,15 +1,20 @@
use crossterm::{
cursor,
event::{self, DisableMouseCapture, EnableMouseCapture},
event::{self, DisableMouseCapture, EnableMouseCapture, EventStream},
execute,
terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};
use async_std::channel::unbounded;
use async_std::sync::Arc;
use async_std::task;
use futures::prelude::*;
use futures::{future::FutureExt, select, StreamExt};
use futures_timer::Delay;
use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{sync::mpsc, thread, time::Duration, time::Instant};
use std::time::{Duration, Instant};
use serde::{Deserialize, Serialize};
@ -62,38 +67,36 @@ pub fn destruct_terminal() {
}
pub struct Events {
pub rx: mpsc::Receiver<Event<Key>>,
pub tx: mpsc::Sender<Event<Key>>,
pub rx: async_std::channel::Receiver<Event<Key>>,
pub pause_stdin: Arc<AtomicBool>,
pub handle: Option<thread::JoinHandle<()>>,
}
impl Events {
#[cfg(feature = "crossterm")]
pub fn with_config(config: EventConfig) -> Events {
use crossterm::event::{KeyCode::*, KeyModifiers};
let (tx, rx) = mpsc::channel();
let pause_stdin = Arc::new(AtomicBool::new(false));
let tick_rate = config.tick_rate;
let handle = Some({
let tx = tx.clone();
let pause_stdin = pause_stdin.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
let (tx, rx) = unbounded::<Event<Key>>();
let ps = pause_stdin.clone();
task::spawn_local(async move {
let mut reader = EventStream::new();
loop {
if pause_stdin.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(250));
thread::park();
last_tick = Instant::now();
if ps.load(Ordering::SeqCst) {
task::sleep(Duration::from_millis(250)).await;
task::yield_now().await;
continue;
}
let timeout = Duration::from_millis(10)
.checked_sub(last_tick.elapsed())
.unwrap_or_else(|| Duration::from_millis(5));
let mut delay = Delay::new(Duration::from_millis(250)).fuse();
let mut event = reader.next().fuse();
if event::poll(timeout).unwrap() {
if let event::Event::Key(key) = event::read().unwrap() {
select! {
_ = delay => {
tx.send(Event::Tick).await.ok();
},
maybe_event = event => {
if let Some(Ok(event::Event::Key(key))) = maybe_event {
let key = match key.code {
Backspace => Key::Backspace,
Enter => Key::Char('\n'),
@ -119,49 +122,39 @@ impl Events {
_ => Key::Null,
},
};
tx.send(Event::Input(key)).unwrap();
tx.send(Event::Input(key)).await.unwrap();
};
}
}
if last_tick.elapsed() >= tick_rate && tx.send(Event::Tick).is_ok() {
last_tick = Instant::now();
}
}
})
});
Events {
rx,
tx,
pause_stdin,
handle,
}
Events { rx, pause_stdin }
}
/// Attempts to read an event.
/// This function will block the current thread.
pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
self.rx.recv()
pub async fn next(&self) -> Result<Event<Key>, async_std::channel::RecvError> {
self.rx.recv().await
}
pub fn pause_event_loop(&self) {
pub async fn pause_event_loop(&self) {
self.pause_stdin.store(true, Ordering::SeqCst);
thread::yield_now();
task::yield_now().await;
while !self.pause_stdin.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(50));
task::sleep(Duration::from_millis(50)).await;
}
}
pub fn resume_event_loop(&self) {
pub async fn resume_event_loop(&self) {
self.pause_stdin.store(false, Ordering::SeqCst);
thread::yield_now();
task::yield_now().await;
while self.pause_stdin.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(50));
task::sleep(Duration::from_millis(50)).await;
}
self.handle.as_ref().unwrap().thread().unpark();
}
pub fn pause_key_capture(&self, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) {
self.pause_event_loop();
task::block_on(self.pause_event_loop());
disable_raw_mode().unwrap();
execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture).unwrap();
terminal.show_cursor().unwrap();
@ -170,7 +163,7 @@ impl Events {
pub fn resume_key_capture(&self, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) {
execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture).unwrap();
enable_raw_mode().unwrap();
self.resume_event_loop();
task::block_on(self.resume_event_loop());
terminal.resize(terminal.size().unwrap()).unwrap();
}
}