Preparing for v2.0 (#74)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Xithrius 2022-02-16 16:09:39 -08:00 committed by GitHub
parent 5893ae8c60
commit 9ac53ca891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 701 additions and 242 deletions

3
.gitignore vendored
View File

@ -14,9 +14,6 @@
# JetBrains project config folder.
.idea
# Visual Studio Code config folder.
.vscode
# Ignore Vim temporary and swap files.
*.sw?
*~

42
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,42 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'twt'",
"cargo": {
"args": [
"build",
"--bin=twt",
"--package=twitch-tui"
],
"filter": {
"name": "twt",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'twt'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=twt",
"--package=twitch-tui"
],
"filter": {
"name": "twt",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"editor.defaultFormatter": "matklad.rust-analyzer"
}

332
Cargo.lock generated
View File

@ -39,9 +39,9 @@ dependencies = [
[[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 = "bitflags"
@ -63,9 +63,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[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"
[[package]]
name = "cfg-if"
@ -103,9 +103,9 @@ dependencies = [
[[package]]
name = "clipboard-win"
version = "4.2.2"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed"
checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db"
dependencies = [
"error-code",
"str-buf",
@ -114,9 +114,9 @@ dependencies = [
[[package]]
name = "core-foundation"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
@ -290,20 +290,50 @@ dependencies = [
]
[[package]]
name = "error-code"
version = "2.3.0"
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "error-code"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
dependencies = [
"libc",
"str-buf",
]
[[package]]
name = "fd-lock"
version = "3.0.1"
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfc110fe50727d46a428eed832df40affe9bf74d077cac1bf3f2718e823f14c5"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "fd-lock"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcef756dea9cf3db5ce73759cf0467330427a786b47711b8d6c97620d718ceb9"
dependencies = [
"cfg-if",
"libc",
@ -415,10 +445,19 @@ dependencies = [
]
[[package]]
name = "getrandom"
version = "0.2.3"
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]]
name = "getrandom"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [
"cfg-if",
"libc",
@ -452,6 +491,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ef6787e7f0faedc040f95716bdd0e62bcfcf4ba93da053b62dea2691c13864"
dependencies = [
"winapi",
]
[[package]]
name = "irc"
version = "0.15.0"
@ -527,9 +575,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
@ -598,9 +646,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
"bitflags",
"cc",
@ -611,9 +659,9 @@ dependencies = [
[[package]]
name = "ntapi"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
@ -639,9 +687,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@ -649,9 +697,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "openssl"
@ -669,15 +717,15 @@ dependencies = [
[[package]]
name = "openssl-probe"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.71"
version = "0.9.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73"
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
dependencies = [
"autocfg",
"cc",
@ -736,18 +784,18 @@ dependencies = [
[[package]]
name = "pin-project"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08"
checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389"
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
dependencies = [
"proc-macro2",
"quote",
@ -756,9 +804,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "pin-utils"
@ -768,15 +816,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.22"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "proc-macro-error"
@ -804,18 +846,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
@ -830,46 +872,6 @@ dependencies = [
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[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 = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -915,6 +917,20 @@ dependencies = [
"winapi",
]
[[package]]
name = "rustix"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cee647393af53c750e15dcbf7781cdd2e550b246bde76e46c326e7ea3c73773"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"winapi",
]
[[package]]
name = "rustyline"
version = "9.1.2"
@ -957,9 +973,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.3.1"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467"
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
dependencies = [
"bitflags",
"core-foundation",
@ -970,9 +986,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.4.2"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys",
"libc",
@ -1000,9 +1016,9 @@ dependencies = [
[[package]]
name = "signal-hook"
version = "0.3.10"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
dependencies = [
"libc",
"signal-hook-registry",
@ -1036,9 +1052,9 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "smallvec"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "smawk"
@ -1094,9 +1110,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.82"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
@ -1105,13 +1121,13 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.2.0"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
@ -1157,6 +1173,15 @@ dependencies = [
"syn",
]
[[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.1.43"
@ -1264,6 +1289,7 @@ dependencies = [
"crossterm 0.20.0",
"enum-iterator",
"futures",
"fuzzy-matcher",
"irc",
"lazy_static",
"rustyline",
@ -1358,9 +1384,35 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a"
dependencies = [
"windows_aarch64_msvc 0.28.0",
"windows_i686_gnu 0.28.0",
"windows_i686_msvc 0.28.0",
"windows_x86_64_gnu 0.28.0",
"windows_x86_64_msvc 0.28.0",
]
[[package]]
name = "windows-sys"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
"windows_aarch64_msvc 0.32.0",
"windows_i686_gnu 0.32.0",
"windows_i686_msvc 0.32.0",
"windows_x86_64_gnu 0.32.0",
"windows_x86_64_msvc 0.32.0",
]
[[package]]
name = "windows-sys"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
"windows_aarch64_msvc 0.28.0",
"windows_i686_gnu 0.28.0",
@ -1384,9 +1436,9 @@ dependencies = [
[[package]]
name = "windows_aarch64_msvc"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
[[package]]
name = "windows_aarch64_msvc"
@ -1396,9 +1448,27 @@ checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_gnu"
@ -1408,9 +1478,15 @@ checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
@ -1420,9 +1496,15 @@ checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
@ -1432,7 +1514,31 @@ checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.28.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"

View File

@ -28,6 +28,7 @@ rustyline = "9.1.2"
lazy_static = "1.4.0"
structopt = "0.3.26"
enum-iterator = "0.7.0"
fuzzy-matcher = "0.3.7"
[[bin]]
bench = false

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Xithrius
Copyright (c) 2022 Xithrius
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -11,12 +11,14 @@ pub enum State {
Input,
Help,
ChannelSwitch,
Search,
}
#[derive(PartialEq, std::cmp::Eq, std::hash::Hash, IntoEnumIterator)]
pub enum BufferName {
Chat,
Channel,
MessageSearch,
}
pub struct App {
@ -32,6 +34,10 @@ pub struct App {
pub table_constraints: Option<Vec<Constraint>>,
/// The titles of the columns within the table of the terminal
pub column_titles: Option<Vec<String>>,
/// Scrolling offset for windows
pub scroll_offset: usize,
/// A temporary snapshot of current messages
pub messages_snapshot: VecDeque<Data>,
}
impl App {
@ -49,10 +55,16 @@ impl App {
input_buffers: input_buffers_map,
table_constraints: None,
column_titles: None,
scroll_offset: 0,
messages_snapshot: VecDeque::with_capacity(config.terminal.maximum_messages),
}
}
pub fn get_buffer(&mut self) -> &mut LineBuffer {
pub fn current_buffer(&self) -> &LineBuffer {
return self.input_buffers.get(&self.selected_buffer).unwrap();
}
pub fn current_buffer_mut(&mut self) -> &mut LineBuffer {
return self.input_buffers.get_mut(&self.selected_buffer).unwrap();
}
}

View File

@ -9,6 +9,13 @@ use crate::{
utils::{colors::hsl_to_rgb, styles, text::align_text},
};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum PayLoad {
Message(String),
Err(String),
}
#[derive(Debug, Copy, Clone)]
pub struct DataBuilder<'conf> {
pub date_format: &'conf str,
@ -24,7 +31,7 @@ impl<'conf> DataBuilder<'conf> {
time_sent: Local::now().format(self.date_format).to_string(),
author: user,
system: false,
message,
payload: PayLoad::Message(message),
}
}
@ -33,7 +40,7 @@ impl<'conf> DataBuilder<'conf> {
time_sent: Local::now().format(self.date_format).to_string(),
author: "System".to_string(),
system: true,
message,
payload: PayLoad::Message(message),
}
}
@ -42,7 +49,7 @@ impl<'conf> DataBuilder<'conf> {
time_sent: Local::now().format(self.date_format).to_string(),
author: "Twitch".to_string(),
system: true,
message,
payload: PayLoad::Message(message),
}
}
}
@ -51,8 +58,8 @@ impl<'conf> DataBuilder<'conf> {
pub struct Data {
pub time_sent: String,
pub author: String,
pub message: String,
pub system: bool,
pub payload: PayLoad,
}
impl Data {
@ -77,38 +84,41 @@ impl Data {
}
pub fn to_row(&self, frontend_config: &FrontendConfig, limit: &usize) -> (u16, Row) {
let message = textwrap::fill(self.message.as_str(), *limit);
if let PayLoad::Message(m) = &self.payload {
let message = textwrap::fill(m.as_str(), *limit);
let style;
if self.system {
style = styles::SYSTEM_CHAT;
let style = if self.system {
styles::SYSTEM_CHAT
} else {
Style::default().fg(self.hash_username(&frontend_config.palette))
};
let mut row_vector = vec![
Cell::from(align_text(
&self.author,
frontend_config.username_alignment.as_str(),
frontend_config.maximum_username_length,
))
.style(style),
Cell::from(message.to_string()),
];
if frontend_config.date_shown {
row_vector.insert(0, Cell::from(self.time_sent.to_string()));
}
let msg_height = message.split('\n').count() as u16;
let mut row = Row::new(row_vector).style(styles::CHAT);
if msg_height > 1 {
row = row.height(msg_height);
}
(msg_height, row)
} else {
style = Style::default().fg(self.hash_username(&frontend_config.palette));
panic!("Data.to_row() can only take message payloads.")
}
let mut row_vector = vec![
Cell::from(align_text(
&self.author,
frontend_config.username_alignment.as_str(),
frontend_config.maximum_username_length,
))
.style(style),
Cell::from(message.to_string()),
];
if frontend_config.date_shown {
row_vector.insert(0, Cell::from(self.time_sent.to_string()));
}
let msg_height = message.split('\n').count() as u16;
let mut row = Row::new(row_vector).style(styles::CHAT);
if msg_height > 1 {
row = row.height(msg_height);
}
(msg_height, row)
}
}
@ -123,8 +133,8 @@ mod tests {
Data {
time_sent: Local::now().format("%c").to_string(),
author: "human".to_string(),
message: "beep boop".to_string(),
system: false,
payload: PayLoad::Message("beep boop".to_string()),
}
}

View File

@ -1,3 +1,9 @@
mod handlers;
mod terminal;
mod twitch;
mod ui;
mod utils;
use anyhow::Result;
use structopt::StructOpt;
use tokio::sync::mpsc;
@ -9,12 +15,6 @@ use handlers::{
use crate::handlers::app::App;
mod handlers;
mod terminal;
mod twitch;
mod ui;
mod utils;
#[tokio::main]
async fn main() -> Result<()> {
match CompleteConfig::new() {

View File

@ -18,7 +18,7 @@ use crate::{
handlers::{
app::{App, BufferName, State},
config::CompleteConfig,
data::{Data, DataBuilder},
data::{Data, DataBuilder, PayLoad},
event::{Config, Event, Events, Key},
},
twitch::Action,
@ -96,24 +96,47 @@ pub async fn ui_driver(
};
'outer: loop {
if let Ok(info) = rx.try_recv() {
match info.payload {
PayLoad::Message(_) => app.messages.push_front(info),
// If something such as a keypress failed, fallback to the normal state of the application.
PayLoad::Err(err) => {
app.state = State::Normal;
app.selected_buffer = BufferName::Chat;
app.messages.push_front(data_builder.system(err));
}
}
}
terminal
.draw(|frame| draw_ui(frame, &mut app, &config))
.unwrap();
if let Ok(info) = rx.try_recv() {
app.messages.push_front(info);
}
if let Some(Event::Input(key)) = events.next().await {
match app.state {
State::Input | State::ChannelSwitch => {
let input_buffer = app.get_buffer();
State::Input | State::ChannelSwitch | State::Search => {
let input_buffer = app.current_buffer_mut();
match key {
Key::Up => {
if let State::Input = app.state {
Key::Up => match app.state {
State::Input => {
app.state = State::Normal;
}
State::Search => {
if app.scroll_offset > 1 {
app.scroll_offset -= 1;
}
}
_ => {}
},
Key::Down => {
if let State::Search = app.state {
if app.scroll_offset < app.messages_snapshot.len() {
app.scroll_offset += 1;
}
}
}
Key::Ctrl('f') | Key::Right => {
input_buffer.move_forward(1);
@ -169,6 +192,7 @@ pub async fn ui_driver(
.await
.unwrap();
}
input_message.update("", 0);
}
BufferName::Channel => {
@ -184,11 +208,13 @@ pub async fn ui_driver(
config.twitch.channel = input_message.to_string();
}
input_message.update("", 0);
app.selected_buffer = BufferName::Chat;
app.state = State::Normal;
}
BufferName::MessageSearch => {}
},
Key::Char(c) => {
input_buffer.insert(c, 1);
@ -205,12 +231,20 @@ pub async fn ui_driver(
app.state = State::Normal;
app.selected_buffer = BufferName::Chat;
}
Key::Char('?') => app.state = State::Help,
Key::Char('i') => app.state = State::Input,
Key::Char('C') => {
app.state = State::ChannelSwitch;
app.selected_buffer = BufferName::Channel;
}
Key::Char('?') => app.state = State::Help,
Key::Char('i') => {
app.state = State::Input;
app.selected_buffer = BufferName::Chat;
}
Key::Char('s') => {
app.state = State::Search;
app.selected_buffer = BufferName::MessageSearch;
app.messages_snapshot = app.messages.clone();
}
Key::Char('q') => {
quitting(terminal);
break 'outer;
@ -220,11 +254,10 @@ pub async fn ui_driver(
quitting(terminal);
break 'outer;
}
State::ChannelSwitch => {
app.selected_buffer = BufferName::Chat;
_ => {
app.state = State::Normal;
app.selected_buffer = BufferName::Chat;
}
_ => app.state = State::Normal,
},
_ => {}
},

View File

@ -29,7 +29,9 @@ pub async fn twitch_irc(mut config: CompleteConfig, tx: Sender<Data>, mut rx: Re
};
let mut client = Client::from_config(irc_config.clone()).await.unwrap();
client.identify().unwrap();
let mut stream = client.stream().unwrap();
let data_builder = DataBuilder::new(&config.frontend.date_format);
let mut room_state_startup = false;
@ -57,17 +59,19 @@ pub async fn twitch_irc(mut config: CompleteConfig, tx: Sender<Data>, mut rx: Re
biased;
Some(action) = rx.recv() => {
let current_channel = format!("#{}", config.twitch.channel);
match action {
Action::Privmsg(message) => {
client
.send_privmsg(format!("#{}", config.twitch.channel), message)
.send_privmsg(current_channel, message)
.unwrap();
}
Action::Join(channel) => {
let channel_list = format!("#{}", channel);
// Leave previous channel
if let Err(err) = client.send_part(format!("#{}", config.twitch.channel)) {
if let Err(err) = client.send_part(current_channel) {
tx.send(data_builder.twitch(err.to_string())).await.unwrap()
} else {
tx.send(data_builder.twitch(format!("Joined {}", channel_list))).await.unwrap();
@ -85,7 +89,7 @@ pub async fn twitch_irc(mut config: CompleteConfig, tx: Sender<Data>, mut rx: Re
}
Some(_message) = stream.next() => {
if let Ok(message) = _message {
let mut tags: HashMap<&str, &str> = std::collections::HashMap::new();
let mut tags: HashMap<&str, &str> = HashMap::new();
if let Some(ref _tags) = message.tags {
for tag in _tags {
if let Some(ref tag_value) = tag.1 {

View File

@ -8,7 +8,7 @@ use tui::{
style::{Color, Modifier, Style},
terminal::Frame,
text::{Span, Spans},
widgets::{Block, Borders, Clear, Paragraph, Row, Table},
widgets::{Block, Borders, Paragraph, Row, Table},
};
use crate::{
@ -16,10 +16,7 @@ use crate::{
app::{App, State},
config::CompleteConfig,
},
ui::{
popups::{centered_popup, Centering},
statics::COMMANDS,
},
ui::statics::COMMANDS,
utils::{styles, text::get_cursor_position},
};
@ -122,7 +119,7 @@ pub fn draw_ui<T: Backend>(frame: &mut Frame<T>, app: &mut App, config: &Complet
match app.state {
State::Input => {
let input_buffer = app.get_buffer();
let input_buffer = app.current_buffer();
if input_buffer.starts_with('/') {
let suggested_commands = COMMANDS
@ -165,31 +162,9 @@ pub fn draw_ui<T: Backend>(frame: &mut Frame<T>, app: &mut App, config: &Complet
vertical_chunks[vertical_chunk_constraints.len() - 1],
);
}
State::Help => popups::help::keybinds(frame),
State::ChannelSwitch => {
let input_rect = centered_popup(Centering::Input(30, 10), frame.size());
let input_buffer = app.get_buffer();
let cursor_pos = get_cursor_position(input_buffer);
frame.set_cursor(
(input_rect.x + cursor_pos as u16 + 1)
.min(input_rect.x + input_rect.width.saturating_sub(2)),
input_rect.y + 1,
);
let paragraph = Paragraph::new(input_buffer.as_str())
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("[ Channel ]"))
.scroll((
0,
((cursor_pos + 3) as u16).saturating_sub(input_rect.width),
));
frame.render_widget(Clear, input_rect);
frame.render_widget(paragraph, input_rect);
}
_ => (),
State::Help => popups::help::show_keybinds(frame),
State::ChannelSwitch => popups::channels::switch_channels(frame, app),
State::Search => popups::messages::search_messages(frame, app),
_ => {}
}
}

37
src/ui/popups/channels.rs Normal file
View File

@ -0,0 +1,37 @@
use tui::{
backend::Backend,
style::{Color, Style},
terminal::Frame,
widgets::{Block, Borders, Clear, Paragraph},
};
use crate::{
handlers::app::App,
ui::popups::{centered_popup, WindowType},
utils::text::get_cursor_position,
};
pub fn switch_channels<T: Backend>(frame: &mut Frame<T>, app: &mut App) {
let input_rect = centered_popup(WindowType::Input(frame.size().height), frame.size());
let input_buffer = app.current_buffer();
let cursor_pos = get_cursor_position(input_buffer);
frame.set_cursor(
(input_rect.x + cursor_pos as u16 + 1)
.min(input_rect.x + input_rect.width.saturating_sub(2)),
input_rect.y + 1,
);
let paragraph = Paragraph::new(input_buffer.as_str())
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("[ Channel ]"))
.scroll((
0,
((cursor_pos + 3) as u16).saturating_sub(input_rect.width),
));
frame.render_widget(Clear, input_rect);
frame.render_widget(paragraph, input_rect);
}

View File

@ -7,13 +7,13 @@ use tui::{
use crate::{
ui::{
popups::{centered_popup, Centering},
popups::{centered_popup, Centering, WindowType},
statics::{HELP_COLUMN_TITLES, HELP_KEYBINDS},
},
utils::{styles, text::vector_column_max},
};
pub fn keybinds<T: Backend>(frame: &mut Frame<T>) {
pub fn show_keybinds<T: Backend>(frame: &mut Frame<T>) {
let table_widths = vector_column_max(&HELP_KEYBINDS, None)
.into_iter()
.map(Constraint::Min)
@ -26,7 +26,13 @@ pub fn keybinds<T: Backend>(frame: &mut Frame<T>) {
.column_spacing(2)
.style(styles::BORDER_NAME);
let area = centered_popup(Centering::Window(60, 50), frame.size());
let area = centered_popup(
WindowType::Window(
Centering::Middle(frame.size().height),
HELP_KEYBINDS.len() as u16,
),
frame.size(),
);
frame.render_widget(Clear, area);
frame.render_widget(help_table, area);

142
src/ui/popups/messages.rs Normal file
View File

@ -0,0 +1,142 @@
use std::collections::VecDeque;
use tui::{
backend::Backend,
layout::Constraint,
style::{Color, Modifier, Style},
terminal::Frame,
text::{Span, Spans},
widgets::{Block, Borders, Clear, Paragraph, Row, Table},
};
use fuzzy_matcher::skim::SkimMatcherV2;
use lazy_static::lazy_static;
use crate::{
handlers::{app::App, data::PayLoad},
ui::popups::{centered_popup, scroll_view, Centering, WindowType},
utils::{styles, text::get_cursor_position},
};
use fuzzy_matcher::FuzzyMatcher;
const MAX_MESSAGE_SEARCH: u16 = 10;
lazy_static! {
pub static ref FUZZY_FINDER: SkimMatcherV2 = SkimMatcherV2::default();
}
pub fn search_messages<T: Backend>(frame: &mut Frame<T>, app: &mut App) {
let input_rect = centered_popup(WindowType::Input(frame.size().height), frame.size());
let window_rect = centered_popup(
WindowType::Window(Centering::Height(frame.size().height), MAX_MESSAGE_SEARCH),
frame.size(),
);
let input_buffer = app.current_buffer();
let cursor_pos = get_cursor_position(input_buffer);
frame.set_cursor(
(input_rect.x + cursor_pos as u16 + 1)
.min(input_rect.x + input_rect.width.saturating_sub(2)),
input_rect.y + 1,
);
let input_text = &input_buffer.as_str();
let input_paragraph = Paragraph::new(*input_text)
.style(Style::default().fg(Color::Yellow))
.block(
Block::default()
.borders(Borders::ALL)
.title("[ Message Search ]"),
)
.scroll((
0,
((cursor_pos + 3) as u16).saturating_sub(input_rect.width),
));
frame.render_widget(Clear, input_rect);
frame.render_widget(input_paragraph, input_rect);
let all_messages = app
.messages_snapshot
.iter()
.flat_map(|f| match &f.payload {
PayLoad::Message(m) => Some(m.as_str()),
_ => None,
})
.collect::<VecDeque<&str>>();
if all_messages.is_empty() {
let window_paragraph = Table::new(vec![])
.block(Block::default().borders(Borders::ALL).title("[ Results ]"))
.column_spacing(2)
.style(styles::BORDER_NAME);
frame.render_widget(Clear, window_rect);
frame.render_widget(window_paragraph, window_rect);
return;
}
let maximum_message_length = *all_messages
.iter()
.map(|v| v.len())
.collect::<Vec<usize>>()
.iter()
.max()
.unwrap() as u16;
let table_widths = all_messages
.iter()
.map(|_| Constraint::Min(maximum_message_length))
.collect::<Vec<Constraint>>();
let render_messages = scroll_view(all_messages, app.scroll_offset, MAX_MESSAGE_SEARCH as usize);
let rows = if input_text.is_empty() {
render_messages
.iter()
.map(|&v| Row::new(vec![v]))
.collect::<Vec<Row>>()
} else {
render_messages
.iter()
.flat_map(|&f| {
let chars = f.chars();
if let Some((_, indices)) = FUZZY_FINDER.fuzzy_indices(f, input_text) {
Some(Row::new(vec![Spans::from(
chars
.enumerate()
.map(|(i, s)| {
if indices.contains(&i) {
Span::styled(
s.to_string(),
Style::default()
.fg(Color::Red)
.add_modifier(Modifier::BOLD),
)
} else {
Span::raw(s.to_string())
}
})
.collect::<Vec<Span>>(),
)]))
} else {
None
}
})
.collect::<Vec<Row>>()
};
let window_paragraph = Table::new(rows)
.block(Block::default().borders(Borders::ALL).title("[ Results ]"))
.column_spacing(2)
.widths(&table_widths)
.style(styles::BORDER_NAME);
frame.render_widget(Clear, window_rect);
frame.render_widget(window_paragraph, window_rect);
}

View File

@ -1,22 +1,40 @@
pub mod channels;
pub mod help;
pub mod messages;
use std::collections::VecDeque;
use tui::layout::{Constraint, Direction, Layout, Rect};
const HORIZONTAL_CONSTRAINTS: [Constraint; 3] = [
Constraint::Percentage(15),
Constraint::Percentage(70),
Constraint::Percentage(15),
];
pub enum Centering {
Input(u16, u16),
Window(u16, u16),
Height(u16),
Middle(u16),
}
pub fn centered_popup(c: Centering, size: Rect) -> Rect {
pub enum WindowType {
/// An input window, with the integer representing the height of the terminal
Input(u16),
/// A window containing either some specified terminal height, or in the middle,
/// with an integer describing the amount of vertically stored items
Window(Centering, u16),
}
pub fn centered_popup(c: WindowType, size: Rect) -> Rect {
match c {
Centering::Window(percent_x, percent_y) => {
WindowType::Input(v) => {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Length((v / 2) as u16 - 6),
Constraint::Length(3),
Constraint::Min(0),
]
.as_ref(),
)
@ -24,40 +42,113 @@ pub fn centered_popup(c: Centering, size: Rect) -> Rect {
Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.constraints(HORIZONTAL_CONSTRAINTS.as_ref())
.split(popup_layout[1])[1]
}
Centering::Input(percent_x, percent_y) => {
WindowType::Window(v, i) => {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Length(3),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.constraints([
Constraint::Length(match v {
Centering::Height(terminal_height) => (terminal_height / 2) as u16 - 3,
Centering::Middle(terminal_height) => ((terminal_height - i) / 2) as u16,
}),
Constraint::Length(i),
match v {
Centering::Height(_) => Constraint::Min(0),
Centering::Middle(terminal_height) => {
Constraint::Length(((terminal_height - i) / 2) as u16 - 3)
}
},
])
.split(size);
Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.constraints(HORIZONTAL_CONSTRAINTS.as_ref())
.split(popup_layout[1])[1]
}
}
}
pub fn scroll_view<T: std::marker::Copy>(
v: VecDeque<T>,
offset: usize,
amount: usize,
) -> VecDeque<T> {
if offset > v.len() {
panic!(
"Scroll offset is {}, but length of VecDeque is {}.",
offset,
v.len()
);
}
// If the offset is at 0 or at the bottom of the input, then there's no need to move.
else if (offset == 0 && amount == v.len()) || v.is_empty() {
v
}
// If there's no amount specified, return everything behind and including the offset index values,
// or when the offset and the amount wanted goes over the length of the VecDeque.
else if amount == 0 || offset + amount > v.len() {
v.range(offset..).copied().collect::<VecDeque<T>>()
} else {
v.range(offset..offset + amount)
.copied()
.collect::<VecDeque<T>>()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn setup() -> VecDeque<i32> {
VecDeque::from([1, 2, 3, 4, 5])
}
#[test]
#[should_panic(expected = "Scroll offset is 10, but length of VecDeque is 5.")]
fn test_offset_plus_amount_over_length() {
scroll_view(setup(), 10, 3);
}
#[test]
#[should_panic(expected = "Scroll offset is 3, but length of VecDeque is 0.")]
fn test_zero_length_input_some_offset() {
scroll_view(vec![].iter().copied().collect::<VecDeque<i32>>(), 3, 0);
}
#[test]
fn test_zero_length_input_no_offset() {
let empty_deq: VecDeque<i32> = scroll_view(VecDeque::from([]), 0, 3);
assert_eq!(empty_deq, VecDeque::from([]));
}
#[test]
fn test_no_offset_no_amount() {
let empty_deq: VecDeque<i32> = scroll_view(VecDeque::from([]), 0, 0);
assert_eq!(empty_deq, VecDeque::from([]));
}
#[test]
fn test_offset_1_all_elements() {
assert_eq!(scroll_view(setup(), 1, 0), VecDeque::from([2, 3, 4, 5]));
}
#[test]
fn test_no_offset_some_amount() {
assert_eq!(scroll_view(setup(), 0, 3), VecDeque::from([1, 2, 3]));
}
#[test]
fn test_some_offset_some_amount() {
assert_eq!(scroll_view(setup(), 2, 2), VecDeque::from([3, 4]));
}
#[test]
fn test_offset_and_amount_centered() {
assert_eq!(scroll_view(setup(), 1, 3), VecDeque::from([2, 3, 4]));
}
}