mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-23 11:07:45 +02:00
WIP
This commit is contained in:
parent
0d89f657d5
commit
951939efb8
8 changed files with 1024 additions and 1088 deletions
298
Cargo.lock
generated
298
Cargo.lock
generated
|
@ -17,17 +17,6 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.5"
|
||||
|
@ -101,15 +90,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.73"
|
||||
name = "atomic"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
|
@ -132,12 +116,6 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -153,15 +131,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
|
@ -312,40 +281,12 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"json5",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"pathdiff",
|
||||
"ron",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.5.11",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
|
@ -373,16 +314,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.4"
|
||||
|
@ -455,16 +386,6 @@ version = "0.1.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "5.0.1"
|
||||
|
@ -495,12 +416,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
|
@ -571,6 +486,20 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "figment"
|
||||
version = "0.10.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"pear",
|
||||
"serde",
|
||||
"toml",
|
||||
"uncased",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -675,16 +604,6 @@ dependencies = [
|
|||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
|
@ -702,15 +621,6 @@ version = "0.28.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
|
@ -750,7 +660,7 @@ dependencies = [
|
|||
"os_info",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml 0.7.6",
|
||||
"toml",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -796,7 +706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -805,6 +715,12 @@ version = "2.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c785eefb63ebd0e33416dfcb8d6da0bf27ce752843a45632a67bf10d4d4b5c4"
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
|
@ -840,17 +756,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -863,12 +768,6 @@ version = "0.2.147"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
|
@ -1020,16 +919,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.7.0"
|
||||
|
@ -1089,56 +978,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
name = "pear"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33"
|
||||
checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
"inlinable_string",
|
||||
"pear_codegen",
|
||||
"yansi 1.0.0-rc.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.3"
|
||||
name = "pear_codegen"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a"
|
||||
checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.12"
|
||||
|
@ -1164,7 +1025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
"yansi 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1176,6 +1037,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"version_check",
|
||||
"yansi 1.0.0-rc.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
|
@ -1329,27 +1203,6 @@ version = "0.7.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags 1.3.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
|
@ -1476,17 +1329,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
|
@ -1661,9 +1503,9 @@ dependencies = [
|
|||
"clap",
|
||||
"clap_complete",
|
||||
"color-eyre",
|
||||
"config",
|
||||
"crossterm",
|
||||
"directories",
|
||||
"figment",
|
||||
"futures",
|
||||
"human-panic",
|
||||
"itertools",
|
||||
|
@ -1686,6 +1528,7 @@ dependencies = [
|
|||
"task-hookrs",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
|
@ -1792,15 +1635,6 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.6"
|
||||
|
@ -1934,16 +1768,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
name = "uncased"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||
checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
|
@ -2211,17 +2042,14 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
|
||||
|
|
|
@ -17,9 +17,9 @@ cassowary = "0.3.0"
|
|||
chrono = "0.4.28"
|
||||
clap = { version = "4.4.2", features = ["std", "color", "help", "usage", "error-context", "suggestions", "derive", "cargo", "wrap_help", "unicode", "string", "unstable-styles"] }
|
||||
color-eyre = "0.6.2"
|
||||
config = "0.13.3"
|
||||
crossterm = { version = "0.27.0", features = ["event-stream", "serde"] }
|
||||
directories = "5.0.1"
|
||||
figment = { version = "0.10.10", features = ["toml", "env"] }
|
||||
futures = "0.3.28"
|
||||
human-panic = "1.2.0"
|
||||
itertools = "0.11.0"
|
||||
|
@ -42,6 +42,7 @@ strip-ansi-escapes = "0.2.0"
|
|||
task-hookrs = "0.9.0"
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tokio-util = "0.7.8"
|
||||
toml = "0.7.6"
|
||||
tracing = "0.1.37"
|
||||
tracing-error = "0.2.0"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
|
|
|
@ -1,5 +1,32 @@
|
|||
#[derive(Clone, PartialEq, Eq, Debug, Copy)]
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
Quit,
|
||||
Refresh,
|
||||
GotoBottom,
|
||||
GotoTop,
|
||||
GotoPageBottom,
|
||||
GotoPageTop,
|
||||
Down,
|
||||
Up,
|
||||
PageDown,
|
||||
PageUp,
|
||||
Delete,
|
||||
Done,
|
||||
ToggleStartStop,
|
||||
QuickTag,
|
||||
Select,
|
||||
SelectAll,
|
||||
Undo,
|
||||
Edit,
|
||||
Shell,
|
||||
Help,
|
||||
ToggleZoom,
|
||||
Context,
|
||||
Next,
|
||||
Previous,
|
||||
Shortcut(usize),
|
||||
Report,
|
||||
Filter,
|
||||
Add,
|
||||
|
|
37
src/app.rs
37
src/app.rs
|
@ -13,7 +13,7 @@ use std::{
|
|||
use chrono::{DateTime, Datelike, FixedOffset, Local, NaiveDate, NaiveDateTime, TimeZone, Timelike};
|
||||
use color_eyre::eyre::{anyhow, Context as AnyhowContext, Result};
|
||||
use crossterm::{
|
||||
event::{DisableMouseCapture, EnableMouseCapture, KeyCode, KeyModifiers},
|
||||
event::{DisableMouseCapture, EnableMouseCapture, KeyCode, KeyEvent, KeyModifiers},
|
||||
execute,
|
||||
style::style,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
|
@ -211,7 +211,7 @@ pub struct TaskwarriorTui {
|
|||
}
|
||||
|
||||
impl TaskwarriorTui {
|
||||
pub async fn new(report: &str, init_event_loop: bool) -> Result<Self> {
|
||||
pub async fn new(report: &str) -> Result<Self> {
|
||||
let output = std::process::Command::new("task")
|
||||
.arg("rc.color=off")
|
||||
.arg("rc._forcecolor=off")
|
||||
|
@ -360,7 +360,7 @@ impl TaskwarriorTui {
|
|||
match event {
|
||||
Event::Key(keyevent) => {
|
||||
debug!("Received input = {:?}", keyevent);
|
||||
self.handle_input(keyevent.code).await?;
|
||||
self.handle_input(keyevent).await?;
|
||||
}
|
||||
Event::Mouse(mouseevent) => {
|
||||
debug!("tui mouseevent")
|
||||
|
@ -2405,7 +2405,7 @@ impl TaskwarriorTui {
|
|||
es
|
||||
}
|
||||
|
||||
pub async fn handle_input(&mut self, input: KeyCode) -> Result<()> {
|
||||
pub async fn handle_input(&mut self, input: KeyEvent) -> Result<()> {
|
||||
match self.mode {
|
||||
Mode::Tasks(_) => {
|
||||
self.handle_input_by_task_mode(input).await?;
|
||||
|
@ -2462,7 +2462,7 @@ impl TaskwarriorTui {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_input_by_task_mode(&mut self, input: KeyCode) -> Result<()> {
|
||||
async fn handle_input_by_task_mode(&mut self, input: KeyEvent) -> Result<()> {
|
||||
if let Mode::Tasks(task_mode) = &self.mode {
|
||||
match task_mode {
|
||||
Action::Report => {
|
||||
|
@ -2923,7 +2923,7 @@ impl TaskwarriorTui {
|
|||
}
|
||||
_ => {
|
||||
self.command_history.reset();
|
||||
handle_movement(&mut self.modify, input, &mut self.changes);
|
||||
handle_movement(&mut self.modify, input);
|
||||
self.update_input_for_completion();
|
||||
}
|
||||
},
|
||||
|
@ -2950,7 +2950,7 @@ impl TaskwarriorTui {
|
|||
self.reset_command();
|
||||
self.mode = Mode::Tasks(Action::Report);
|
||||
}
|
||||
_ => handle_movement(&mut self.command, input, &mut self.changes),
|
||||
_ => handle_movement(&mut self.command, input),
|
||||
},
|
||||
Action::Log => match input {
|
||||
KeyCode::Esc => {
|
||||
|
@ -3056,7 +3056,7 @@ impl TaskwarriorTui {
|
|||
}
|
||||
_ => {
|
||||
self.command_history.reset();
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
self.update_input_for_completion();
|
||||
}
|
||||
},
|
||||
|
@ -3164,7 +3164,7 @@ impl TaskwarriorTui {
|
|||
|
||||
_ => {
|
||||
self.command_history.reset();
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
self.update_input_for_completion();
|
||||
}
|
||||
},
|
||||
|
@ -3192,7 +3192,7 @@ impl TaskwarriorTui {
|
|||
self.reset_command();
|
||||
self.mode = Mode::Tasks(Action::Report);
|
||||
}
|
||||
_ => handle_movement(&mut self.command, input, &mut self.changes),
|
||||
_ => handle_movement(&mut self.command, input),
|
||||
},
|
||||
Action::Add => match input {
|
||||
KeyCode::Esc => {
|
||||
|
@ -3298,7 +3298,7 @@ impl TaskwarriorTui {
|
|||
}
|
||||
_ => {
|
||||
self.command_history.reset();
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
self.update_input_for_completion();
|
||||
}
|
||||
},
|
||||
|
@ -3412,7 +3412,7 @@ impl TaskwarriorTui {
|
|||
// self.dirty = true;
|
||||
// }
|
||||
_ => {
|
||||
handle_movement(&mut self.filter, input, &mut self.changes);
|
||||
handle_movement(&mut self.filter, input);
|
||||
self.update_input_for_completion();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
@ -3437,7 +3437,7 @@ impl TaskwarriorTui {
|
|||
} else if input == self.keyconfig.quit || input == KeyCode::Esc {
|
||||
self.mode = Mode::Tasks(Action::Report);
|
||||
} else {
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
}
|
||||
}
|
||||
Action::DeletePrompt => {
|
||||
|
@ -3460,7 +3460,7 @@ impl TaskwarriorTui {
|
|||
} else if input == self.keyconfig.quit || input == KeyCode::Esc {
|
||||
self.mode = Mode::Tasks(Action::Report);
|
||||
} else {
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
}
|
||||
}
|
||||
Action::UndoPrompt => {
|
||||
|
@ -3483,7 +3483,7 @@ impl TaskwarriorTui {
|
|||
} else if input == self.keyconfig.quit || input == KeyCode::Esc {
|
||||
self.mode = Mode::Tasks(Action::Report);
|
||||
} else {
|
||||
handle_movement(&mut self.command, input, &mut self.changes);
|
||||
handle_movement(&mut self.command, input);
|
||||
}
|
||||
}
|
||||
Action::Error => {
|
||||
|
@ -3639,11 +3639,8 @@ impl TaskwarriorTui {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_movement(linebuffer: &mut Input, input: KeyCode, changes: &mut utils::Changeset) {
|
||||
linebuffer.handle_event(&crossterm::event::Event::Key(crossterm::event::KeyEvent::new(
|
||||
input,
|
||||
KeyModifiers::empty(),
|
||||
)));
|
||||
pub fn handle_movement(linebuffer: &mut Input, input: KeyEvent) {
|
||||
linebuffer.handle_event(&crossterm::event::Event::Key(input));
|
||||
}
|
||||
|
||||
pub fn add_tag(task: &mut Task, tag: String) {
|
||||
|
|
1121
src/config.rs
1121
src/config.rs
File diff suppressed because it is too large
Load diff
279
src/keyevent.rs
Normal file
279
src/keyevent.rs
Normal file
|
@ -0,0 +1,279 @@
|
|||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MediaKeyCode, ModifierKeyCode};
|
||||
|
||||
fn parse_key_event(raw: &str) -> Result<KeyEvent, String> {
|
||||
let raw_lower = raw.to_ascii_lowercase();
|
||||
let (remaining, modifiers) = extract_modifiers(&raw_lower);
|
||||
parse_key_code_with_modifiers(remaining, modifiers)
|
||||
}
|
||||
|
||||
fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) {
|
||||
let mut modifiers = KeyModifiers::empty();
|
||||
let mut current = raw;
|
||||
|
||||
loop {
|
||||
match current {
|
||||
rest if rest.starts_with("ctrl-") => {
|
||||
modifiers.insert(KeyModifiers::CONTROL);
|
||||
current = &rest[5..];
|
||||
}
|
||||
rest if rest.starts_with("alt-") => {
|
||||
modifiers.insert(KeyModifiers::ALT);
|
||||
current = &rest[4..];
|
||||
}
|
||||
rest if rest.starts_with("shift-") => {
|
||||
modifiers.insert(KeyModifiers::SHIFT);
|
||||
current = &rest[6..];
|
||||
}
|
||||
_ => break, // break out of the loop if no known prefix is detected
|
||||
};
|
||||
}
|
||||
|
||||
(current, modifiers)
|
||||
}
|
||||
|
||||
fn parse_key_code_with_modifiers(raw: &str, mut modifiers: KeyModifiers) -> Result<KeyEvent, String> {
|
||||
let c = match raw {
|
||||
"esc" => KeyCode::Esc,
|
||||
"enter" => KeyCode::Enter,
|
||||
"left" => KeyCode::Left,
|
||||
"right" => KeyCode::Right,
|
||||
"up" => KeyCode::Up,
|
||||
"down" => KeyCode::Down,
|
||||
"home" => KeyCode::Home,
|
||||
"end" => KeyCode::End,
|
||||
"pageup" => KeyCode::PageUp,
|
||||
"pagedown" => KeyCode::PageDown,
|
||||
"backtab" => {
|
||||
modifiers.insert(KeyModifiers::SHIFT);
|
||||
KeyCode::BackTab
|
||||
}
|
||||
"backspace" => KeyCode::Backspace,
|
||||
"delete" => KeyCode::Delete,
|
||||
"insert" => KeyCode::Insert,
|
||||
"f1" => KeyCode::F(1),
|
||||
"f2" => KeyCode::F(2),
|
||||
"f3" => KeyCode::F(3),
|
||||
"f4" => KeyCode::F(4),
|
||||
"f5" => KeyCode::F(5),
|
||||
"f6" => KeyCode::F(6),
|
||||
"f7" => KeyCode::F(7),
|
||||
"f8" => KeyCode::F(8),
|
||||
"f9" => KeyCode::F(9),
|
||||
"f10" => KeyCode::F(10),
|
||||
"f11" => KeyCode::F(11),
|
||||
"f12" => KeyCode::F(12),
|
||||
"space" => KeyCode::Char(' '),
|
||||
"tab" => KeyCode::Tab,
|
||||
c if c.len() == 1 => {
|
||||
let mut c = c.chars().next().unwrap();
|
||||
if modifiers.contains(KeyModifiers::SHIFT) {
|
||||
c = c.to_ascii_uppercase();
|
||||
}
|
||||
KeyCode::Char(c)
|
||||
}
|
||||
_ => return Err(format!("Unable to parse {raw}")),
|
||||
};
|
||||
Ok(KeyEvent::new(c, modifiers))
|
||||
}
|
||||
|
||||
pub fn parse_key_sequence(raw: &str) -> Result<Vec<KeyEvent>, String> {
|
||||
if raw.chars().filter(|c| *c == '>').count() != raw.chars().filter(|c| *c == '<').count() {
|
||||
return Err(format!("Unable to parse `{}`", raw));
|
||||
}
|
||||
let raw = if !raw.contains("><") {
|
||||
let raw = raw.strip_prefix("<").unwrap_or(raw);
|
||||
let raw = raw.strip_prefix(">").unwrap_or(raw);
|
||||
raw
|
||||
} else {
|
||||
raw
|
||||
};
|
||||
let sequences = raw
|
||||
.split("><")
|
||||
.map(|seq| {
|
||||
if seq.starts_with('<') {
|
||||
&seq[1..]
|
||||
} else if seq.ends_with('>') {
|
||||
&seq[..seq.len() - 1]
|
||||
} else {
|
||||
seq
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
sequences.into_iter().map(parse_key_event).collect()
|
||||
}
|
||||
|
||||
pub fn key_event_to_string(event: KeyEvent) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
result.push('<');
|
||||
|
||||
// Add modifiers
|
||||
if event.modifiers.contains(KeyModifiers::CONTROL) {
|
||||
result.push_str("ctrl-");
|
||||
}
|
||||
if event.modifiers.contains(KeyModifiers::ALT) {
|
||||
result.push_str("alt-");
|
||||
}
|
||||
if event.modifiers.contains(KeyModifiers::SHIFT) {
|
||||
result.push_str("shift-");
|
||||
}
|
||||
|
||||
match event.code {
|
||||
KeyCode::Char(' ') => result.push_str("space"),
|
||||
KeyCode::Char(c) => result.push(c),
|
||||
KeyCode::Enter => result.push_str("enter"),
|
||||
KeyCode::Esc => result.push_str("esc"),
|
||||
KeyCode::Left => result.push_str("left"),
|
||||
KeyCode::Right => result.push_str("right"),
|
||||
KeyCode::Up => result.push_str("up"),
|
||||
KeyCode::Down => result.push_str("down"),
|
||||
KeyCode::Home => result.push_str("home"),
|
||||
KeyCode::End => result.push_str("end"),
|
||||
KeyCode::PageUp => result.push_str("pageup"),
|
||||
KeyCode::PageDown => result.push_str("pagedown"),
|
||||
KeyCode::BackTab => result.push_str("backtab"),
|
||||
KeyCode::Delete => result.push_str("delete"),
|
||||
KeyCode::Insert => result.push_str("insert"),
|
||||
KeyCode::F(n) => result.push_str(&format!("f{}", n)),
|
||||
KeyCode::Backspace => result.push_str("backspace"),
|
||||
KeyCode::Tab => result.push_str("tab"),
|
||||
KeyCode::Null => result.push_str("null"),
|
||||
KeyCode::CapsLock => result.push_str("capslock"),
|
||||
KeyCode::ScrollLock => result.push_str("scrolllock"),
|
||||
KeyCode::NumLock => result.push_str("numlock"),
|
||||
KeyCode::PrintScreen => result.push_str("printscreen"),
|
||||
KeyCode::Pause => result.push_str("pause"),
|
||||
KeyCode::Menu => result.push_str("menu"),
|
||||
KeyCode::KeypadBegin => result.push_str("keypadbegin"),
|
||||
KeyCode::Media(media) => match media {
|
||||
MediaKeyCode::Play => result.push_str("play"),
|
||||
MediaKeyCode::Pause => result.push_str("pause"),
|
||||
MediaKeyCode::PlayPause => result.push_str("playpause"),
|
||||
MediaKeyCode::Reverse => result.push_str("reverse"),
|
||||
MediaKeyCode::Stop => result.push_str("stop"),
|
||||
MediaKeyCode::FastForward => result.push_str("fastforward"),
|
||||
MediaKeyCode::Rewind => result.push_str("rewind"),
|
||||
MediaKeyCode::TrackNext => result.push_str("tracknext"),
|
||||
MediaKeyCode::TrackPrevious => result.push_str("trackprevious"),
|
||||
MediaKeyCode::Record => result.push_str("record"),
|
||||
MediaKeyCode::LowerVolume => result.push_str("lowervolume"),
|
||||
MediaKeyCode::RaiseVolume => result.push_str("raisevolume"),
|
||||
MediaKeyCode::MuteVolume => result.push_str("mutevolume"),
|
||||
},
|
||||
KeyCode::Modifier(keycode) => match keycode {
|
||||
ModifierKeyCode::LeftShift => result.push_str("leftshift"),
|
||||
ModifierKeyCode::LeftControl => result.push_str("leftcontrol"),
|
||||
ModifierKeyCode::LeftAlt => result.push_str("leftalt"),
|
||||
ModifierKeyCode::LeftSuper => result.push_str("leftsuper"),
|
||||
ModifierKeyCode::LeftHyper => result.push_str("lefthyper"),
|
||||
ModifierKeyCode::LeftMeta => result.push_str("leftmeta"),
|
||||
ModifierKeyCode::RightShift => result.push_str("rightshift"),
|
||||
ModifierKeyCode::RightControl => result.push_str("rightcontrol"),
|
||||
ModifierKeyCode::RightAlt => result.push_str("rightalt"),
|
||||
ModifierKeyCode::RightSuper => result.push_str("rightsuper"),
|
||||
ModifierKeyCode::RightHyper => result.push_str("righthyper"),
|
||||
ModifierKeyCode::RightMeta => result.push_str("rightmeta"),
|
||||
ModifierKeyCode::IsoLevel3Shift => result.push_str("isolevel3shift"),
|
||||
ModifierKeyCode::IsoLevel5Shift => result.push_str("isolevel5shift"),
|
||||
},
|
||||
}
|
||||
|
||||
result.push('>');
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn test_event_to_string() {
|
||||
let event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
|
||||
println!("{}", key_event_to_string(event)); // Outputs: ctrl-a
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_key_sequence() {
|
||||
let result = parse_key_sequence("a");
|
||||
assert_eq!(result.unwrap(), vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())]);
|
||||
|
||||
let result = parse_key_sequence("<a><b>");
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty())
|
||||
]
|
||||
);
|
||||
|
||||
let result = parse_key_sequence("<Ctrl-a><Alt-b>");
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::ALT)
|
||||
]
|
||||
);
|
||||
let result = parse_key_sequence("<Ctrl-a>");
|
||||
assert_eq!(result.unwrap(), vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),]);
|
||||
let result = parse_key_sequence("<Ctrl-Alt-a>");
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::ALT),]
|
||||
);
|
||||
assert!(parse_key_sequence("Ctrl-a>").is_err());
|
||||
assert!(parse_key_sequence("<Ctrl-a").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_keys() {
|
||||
assert_eq!(parse_key_event("a").unwrap(), KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()));
|
||||
|
||||
assert_eq!(parse_key_event("enter").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()));
|
||||
|
||||
assert_eq!(parse_key_event("esc").unwrap(), KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_modifiers() {
|
||||
assert_eq!(
|
||||
parse_key_event("ctrl-a").unwrap(),
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)
|
||||
);
|
||||
|
||||
assert_eq!(parse_key_event("alt-enter").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT));
|
||||
|
||||
assert_eq!(parse_key_event("shift-esc").unwrap(), KeyEvent::new(KeyCode::Esc, KeyModifiers::SHIFT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_modifiers() {
|
||||
assert_eq!(
|
||||
parse_key_event("ctrl-alt-a").unwrap(),
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::ALT)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_key_event("ctrl-shift-enter").unwrap(),
|
||||
KeyEvent::new(KeyCode::Enter, KeyModifiers::CONTROL | KeyModifiers::SHIFT)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_keys() {
|
||||
assert!(parse_key_event("invalid-key").is_err());
|
||||
assert!(parse_key_event("ctrl-invalid-key").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_case_insensitivity() {
|
||||
assert_eq!(
|
||||
parse_key_event("CTRL-a").unwrap(),
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)
|
||||
);
|
||||
|
||||
assert_eq!(parse_key_event("AlT-eNtEr").unwrap(), KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT));
|
||||
}
|
||||
}
|
196
src/keymap.rs
Normal file
196
src/keymap.rs
Normal file
|
@ -0,0 +1,196 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::{collections::HashMap, error::Error, str};
|
||||
|
||||
use color_eyre::eyre::{eyre, Context, Result};
|
||||
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
|
||||
use serde::ser::{self, Serialize, SerializeMap, Serializer};
|
||||
|
||||
use crate::keyevent::key_event_to_string;
|
||||
use crate::{action::Action, keyevent::parse_key_sequence};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct KeyMap(pub std::collections::HashMap<Vec<KeyEvent>, Action>);
|
||||
|
||||
impl Deref for KeyMap {
|
||||
type Target = std::collections::HashMap<Vec<KeyEvent>, Action>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for KeyMap {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyMap {
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
let mut sorted_sequences: Vec<_> = self.keys().collect();
|
||||
sorted_sequences.sort_by_key(|seq| seq.len());
|
||||
|
||||
for i in 0..sorted_sequences.len() {
|
||||
for j in i + 1..sorted_sequences.len() {
|
||||
if sorted_sequences[j].starts_with(sorted_sequences[i]) {
|
||||
return Err(format!(
|
||||
"Conflict detected: Sequence {:?} is a prefix of sequence {:?}",
|
||||
sorted_sequences[i], sorted_sequences[j]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for KeyMap {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
// Begin serializing a map.
|
||||
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
||||
|
||||
for (key_sequence, action) in &self.0 {
|
||||
let key_string = key_sequence
|
||||
.iter()
|
||||
.map(|key_event| key_event_to_string(*key_event))
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
|
||||
map.serialize_entry(&key_string, action)?;
|
||||
}
|
||||
|
||||
// End serialization.
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for KeyMap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct KeyMapVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for KeyMapVisitor {
|
||||
type Value = KeyMap;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a keymap with string representation of KeyEvent as key and Action as value")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<KeyMap, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut keymap = std::collections::HashMap::new();
|
||||
|
||||
// While there are entries in the map, read them
|
||||
while let Some((key_sequence_str, action)) = access.next_entry::<String, Action>()? {
|
||||
let key_sequence = parse_key_sequence(&key_sequence_str).map_err(de::Error::custom)?;
|
||||
|
||||
if let Some(old_action) = keymap.insert(key_sequence, action.clone()) {
|
||||
if old_action != action {
|
||||
return Err(format!("Found a {:?} for both {:?} and {:?}", key_sequence_str, old_action, action)).map_err(de::Error::custom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(KeyMap(keymap))
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_map(KeyMapVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod validate_tests {
|
||||
use super::*;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
#[test]
|
||||
fn test_no_conflict() {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
map.insert(vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())], Action::Report);
|
||||
map.insert(vec![KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty())], Action::Report);
|
||||
let keymap = KeyMap(map);
|
||||
|
||||
assert!(keymap.validate().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflict_prefix() {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
map.insert(vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())], Action::Report);
|
||||
map.insert(
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
|
||||
],
|
||||
Action::Report,
|
||||
);
|
||||
let keymap = KeyMap(map);
|
||||
|
||||
assert!(keymap.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_conflict_different_modifiers() {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
map.insert(vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)], Action::Report);
|
||||
map.insert(vec![KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT)], Action::Report);
|
||||
let keymap = KeyMap(map);
|
||||
|
||||
assert!(keymap.validate().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_conflict_multiple_keys() {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
map.insert(
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
|
||||
],
|
||||
Action::Report,
|
||||
);
|
||||
map.insert(
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
],
|
||||
Action::Report,
|
||||
);
|
||||
let keymap = KeyMap(map);
|
||||
|
||||
assert!(keymap.validate().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflict_three_keys() {
|
||||
let mut map = std::collections::HashMap::new();
|
||||
map.insert(
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('c'), KeyModifiers::empty()),
|
||||
],
|
||||
Action::Report,
|
||||
);
|
||||
map.insert(
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),
|
||||
KeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
|
||||
],
|
||||
Action::Report,
|
||||
);
|
||||
let keymap = KeyMap(map);
|
||||
|
||||
assert!(keymap.validate().is_err());
|
||||
}
|
||||
}
|
149
src/main.rs
149
src/main.rs
|
@ -3,21 +3,24 @@
|
|||
#![allow(unused_variables)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
// mod app;
|
||||
// mod calendar;
|
||||
// mod cli;
|
||||
// mod completion;
|
||||
// mod config;
|
||||
// mod help;
|
||||
// mod history;
|
||||
// mod keyconfig;
|
||||
// mod pane;
|
||||
// mod scrollbar;
|
||||
// mod table;
|
||||
// mod task_report;
|
||||
// mod ui;
|
||||
mod action;
|
||||
mod app;
|
||||
mod calendar;
|
||||
mod cli;
|
||||
mod completion;
|
||||
mod config;
|
||||
mod help;
|
||||
mod history;
|
||||
mod keyconfig;
|
||||
mod pane;
|
||||
mod scrollbar;
|
||||
mod table;
|
||||
mod task_report;
|
||||
mod keyevent;
|
||||
mod keymap;
|
||||
mod tui;
|
||||
mod ui;
|
||||
mod utils;
|
||||
|
||||
use std::{
|
||||
|
@ -29,73 +32,73 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use app::{Mode, TaskwarriorTui};
|
||||
// use app::{Mode, TaskwarriorTui};
|
||||
use color_eyre::eyre::Result;
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{DisableMouseCapture, EnableMouseCapture, EventStream},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use futures::stream::{FuturesUnordered, StreamExt};
|
||||
use log::{debug, error, info, log_enabled, trace, warn, Level, LevelFilter};
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
use utils::{get_config_dir, get_data_dir};
|
||||
// use crossterm::{
|
||||
// cursor,
|
||||
// event::{DisableMouseCapture, EnableMouseCapture, EventStream},
|
||||
// execute,
|
||||
// terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
// };
|
||||
// use futures::stream::{FuturesUnordered, StreamExt};
|
||||
// use log::{debug, error, info, log_enabled, trace, warn, Level, LevelFilter};
|
||||
// use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
// use utils::{get_config_dir, get_data_dir};
|
||||
|
||||
use crate::{
|
||||
action::Action,
|
||||
keyconfig::KeyConfig,
|
||||
utils::{initialize_logging, initialize_panic_handler},
|
||||
};
|
||||
|
||||
const LOG_PATTERN: &str = "{d(%Y-%m-%d %H:%M:%S)} | {l} | {f}:{L} | {m}{n}";
|
||||
// use crate::{
|
||||
// action::Action,
|
||||
// keyconfig::KeyConfig,
|
||||
// utils::{initialize_logging, initialize_panic_handler},
|
||||
// };
|
||||
//
|
||||
// const LOG_PATTERN: &str = "{d(%Y-%m-%d %H:%M:%S)} | {l} | {f}:{L} | {m}{n}";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let matches = cli::generate_cli_app().get_matches();
|
||||
|
||||
let config = matches.get_one::<String>("config");
|
||||
let data = matches.get_one::<String>("data");
|
||||
let taskrc = matches.get_one::<String>("taskrc");
|
||||
let taskdata = matches.get_one::<String>("taskdata");
|
||||
let binding = String::from("next");
|
||||
let report = matches.get_one::<String>("report").unwrap_or(&binding);
|
||||
|
||||
let config_dir = config.map(PathBuf::from).unwrap_or_else(get_config_dir);
|
||||
let data_dir = data.map(PathBuf::from).unwrap_or_else(get_data_dir);
|
||||
|
||||
// if let Some(e) = taskrc {
|
||||
// if env::var("TASKRC").is_err() {
|
||||
// // if environment variable is not set, this env::var returns an error
|
||||
// env::set_var("TASKRC", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskrc"))
|
||||
// } else {
|
||||
// warn!("TASKRC environment variable cannot be set.")
|
||||
// }
|
||||
// }
|
||||
// let matches = cli::generate_cli_app().get_matches();
|
||||
//
|
||||
// if let Some(e) = taskdata {
|
||||
// if env::var("TASKDATA").is_err() {
|
||||
// // if environment variable is not set, this env::var returns an error
|
||||
// env::set_var("TASKDATA", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskdata"))
|
||||
// } else {
|
||||
// warn!("TASKDATA environment variable cannot be set.")
|
||||
// }
|
||||
// let config = matches.get_one::<String>("config");
|
||||
// let data = matches.get_one::<String>("data");
|
||||
// let taskrc = matches.get_one::<String>("taskrc");
|
||||
// let taskdata = matches.get_one::<String>("taskdata");
|
||||
// let binding = String::from("next");
|
||||
// let report = matches.get_one::<String>("report").unwrap_or(&binding);
|
||||
//
|
||||
// let config_dir = config.map(PathBuf::from).unwrap_or_else(get_config_dir);
|
||||
// let data_dir = data.map(PathBuf::from).unwrap_or_else(get_data_dir);
|
||||
//
|
||||
// // if let Some(e) = taskrc {
|
||||
// // if env::var("TASKRC").is_err() {
|
||||
// // // if environment variable is not set, this env::var returns an error
|
||||
// // env::set_var("TASKRC", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskrc"))
|
||||
// // } else {
|
||||
// // warn!("TASKRC environment variable cannot be set.")
|
||||
// // }
|
||||
// // }
|
||||
// //
|
||||
// // if let Some(e) = taskdata {
|
||||
// // if env::var("TASKDATA").is_err() {
|
||||
// // // if environment variable is not set, this env::var returns an error
|
||||
// // env::set_var("TASKDATA", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskdata"))
|
||||
// // } else {
|
||||
// // warn!("TASKDATA environment variable cannot be set.")
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// initialize_logging(data_dir)?;
|
||||
// initialize_panic_handler()?;
|
||||
//
|
||||
// log::info!("getting matches from clap...");
|
||||
// debug!("report = {:?}", &report);
|
||||
// debug!("config = {:?}", &config);
|
||||
//
|
||||
// let mut app = app::TaskwarriorTui::new(report).await?;
|
||||
//
|
||||
// let r = app.run().await;
|
||||
//
|
||||
// if let Err(err) = r {
|
||||
// eprintln!("\x1b[0;31m[taskwarrior-tui error]\x1b[0m: {}\n\nIf you need additional help, please report as a github issue on https://github.com/kdheepak/taskwarrior-tui", err);
|
||||
// std::process::exit(1);
|
||||
// }
|
||||
|
||||
initialize_logging(data_dir)?;
|
||||
initialize_panic_handler()?;
|
||||
|
||||
debug!("getting matches from clap...");
|
||||
debug!("report = {:?}", &report);
|
||||
debug!("config = {:?}", &config);
|
||||
|
||||
let mut app = app::TaskwarriorTui::new(report, true).await?;
|
||||
|
||||
let r = app.run().await;
|
||||
|
||||
if let Err(err) = r {
|
||||
eprintln!("\x1b[0;31m[taskwarrior-tui error]\x1b[0m: {}\n\nIf you need additional help, please report as a github issue on https://github.com/kdheepak/taskwarrior-tui", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue