Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
Manos Pitsidianakis | 4bdfb3a31b | |
Manos Pitsidianakis | 671d35e21e | |
Manos Pitsidianakis | a4ebe3b7d4 | |
Manos Pitsidianakis | 57e3e643a1 | |
Manos Pitsidianakis | a8c7582fa3 | |
Manos Pitsidianakis | a9c3b151f1 | |
Manos Pitsidianakis | 1abce964c7 | |
Manos Pitsidianakis | 735b44f286 | |
Manos Pitsidianakis | 50ff16c44f | |
Manos Pitsidianakis | 9ca34a6864 |
2
BUILD.md
2
BUILD.md
|
@ -9,7 +9,7 @@ PREFIX=~/.local make install
|
|||
Available subcommands for `make` are listed with `make help`.
|
||||
The Makefile *should* be POSIX portable and not require a specific `make` version.
|
||||
|
||||
`meli` requires rust version 1.68.2 or later and rust's package manager, Cargo.
|
||||
`meli` requires rust version 1.70.0 or later and rust's package manager, Cargo.
|
||||
Information on how to get it on your system can be found here: <https://doc.rust-lang.org/cargo/getting-started/installation.html>
|
||||
|
||||
With Cargo available, the project can be built with `make` and the resulting binary will then be found under `target/release/meli`.
|
||||
|
|
|
@ -152,7 +152,7 @@ dependencies = [
|
|||
"futures-lite",
|
||||
"rustix 0.37.23",
|
||||
"signal-hook",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -218,6 +218,12 @@ version = "0.21.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64-compat"
|
||||
version = "1.0.0"
|
||||
|
@ -264,10 +270,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "bufstream"
|
||||
version = "0.1.4"
|
||||
name = "bufstream-fresh"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||
checksum = "7c431e5d450eceb6f5096c371f502946ae1cc65407935bc2cae8f1d625a2035f"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
|
@ -624,7 +630,7 @@ checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
|||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -679,7 +685,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1023,7 +1029,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
|||
dependencies = [
|
||||
"hermit-abi 0.3.2",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1206,9 +1212,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mailin"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d685cc697caba9c320fd0be79835c49af79009fccf05e13200d988b07a24b429"
|
||||
checksum = "ff08fefa0913a8fb1cbd31bd83a4841db8b6eb78320d517b31fcf60e2541d57a"
|
||||
dependencies = [
|
||||
"base64-compat",
|
||||
"either",
|
||||
|
@ -1219,12 +1225,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mailin-embedded"
|
||||
version = "0.7.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2f29d14249fb45f7795bc8564175ca7b963254217f24e8cde84ba40d38b58cc"
|
||||
checksum = "143ac497c8c38d22407344e2252767ffd8253593a3d2464acc8619c8ef15835c"
|
||||
dependencies = [
|
||||
"bufstream",
|
||||
"lazy_static",
|
||||
"bufstream-fresh",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"mailin",
|
||||
"rustls",
|
||||
|
@ -1361,7 +1367,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1420,7 +1426,7 @@ dependencies = [
|
|||
"log",
|
||||
"mio",
|
||||
"walkdir",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1625,7 +1631,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1748,17 +1754,17 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
|||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1792,7 +1798,7 @@ dependencies = [
|
|||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1805,28 +1811,48 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.5",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.8"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
|
||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.3"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
|
||||
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
|
||||
dependencies = [
|
||||
"base64 0.21.3",
|
||||
"base64 0.22.1",
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1850,7 +1876,7 @@ version = "0.1.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1865,16 +1891,6 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
|
@ -2036,14 +2052,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stderrlog"
|
||||
|
@ -2082,6 +2098,12 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "svg"
|
||||
version = "0.13.1"
|
||||
|
@ -2130,7 +2152,7 @@ dependencies = [
|
|||
"fastrand 2.0.0",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix 0.38.9",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2355,9 +2377,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
|||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
|
@ -2470,26 +2492,6 @@ version = "0.2.87"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -2540,7 +2542,7 @@ version = "0.48.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2549,7 +2551,16 @@ version = "0.48.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2558,21 +2569,43 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.39.0"
|
||||
|
@ -2585,6 +2618,12 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.39.0"
|
||||
|
@ -2597,6 +2636,18 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.39.0"
|
||||
|
@ -2609,6 +2660,12 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.39.0"
|
||||
|
@ -2621,12 +2678,24 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.39.0"
|
||||
|
@ -2639,6 +2708,12 @@ version = "0.48.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.8"
|
||||
|
@ -2673,3 +2748,9 @@ dependencies = [
|
|||
"quote",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# meli ![Established, created in 2017](https://img.shields.io/badge/Est.-2017-blue) ![Minimum Supported Rust Version](https://img.shields.io/badge/MSRV-1.68.2-blue) [![GitHub license](https://img.shields.io/github/license/meli/meli)](https://github.com/meli/meli/blob/master/COPYING) [![Crates.io](https://img.shields.io/crates/v/meli)](https://crates.io/crates/meli) [![IRC channel](https://img.shields.io/badge/irc.oftc.net-%23meli-blue)](ircs://irc.oftc.net:6697/%23meli)
|
||||
# meli ![Established, created in 2017](https://img.shields.io/badge/Est.-2017-blue) ![Minimum Supported Rust Version](https://img.shields.io/badge/MSRV-1.70.0-blue) [![GitHub license](https://img.shields.io/github/license/meli/meli)](https://github.com/meli/meli/blob/master/COPYING) [![Crates.io](https://img.shields.io/crates/v/meli)](https://crates.io/crates/meli) [![IRC channel](https://img.shields.io/badge/irc.oftc.net-%23meli-blue)](ircs://irc.oftc.net:6697/%23meli)
|
||||
|
||||
**BSD/Linux/macos terminal email client with support for multiple accounts and Maildir / mbox / notmuch / IMAP / JMAP / NNTP (Usenet).**
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "meli"
|
|||
version = "0.8.5"
|
||||
authors = ["Manos Pitsidianakis <manos@pitsidianak.is>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.68.2"
|
||||
rust-version = "1.70.0"
|
||||
license = "GPL-3.0-or-later"
|
||||
readme = "README.md"
|
||||
description = "terminal e-mail client"
|
||||
|
|
|
@ -29,6 +29,7 @@ use std::{
|
|||
collections::HashSet,
|
||||
io::Read,
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use melib::{
|
||||
|
@ -303,24 +304,23 @@ impl From<FileAccount> for AccountConf {
|
|||
}
|
||||
|
||||
pub fn get_config_file() -> Result<PathBuf> {
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").map_err(|err| {
|
||||
Error::new(format!(
|
||||
"Could not detect XDG directories for user: {}",
|
||||
err
|
||||
))
|
||||
.set_source(Some(std::sync::Arc::new(Box::new(err))))
|
||||
})?;
|
||||
match env::var("MELI_CONFIG") {
|
||||
Ok(path) => Ok(PathBuf::from(path).expand()),
|
||||
Err(_) => Ok(xdg_dirs
|
||||
.place_config_file("config.toml")
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
"Cannot create configuration directory in {}",
|
||||
xdg_dirs.get_config_home().display()
|
||||
)
|
||||
})?),
|
||||
if let Ok(path) = env::var("MELI_CONFIG") {
|
||||
return Ok(PathBuf::from(path).expand());
|
||||
}
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").map_err(|err| {
|
||||
Error::new("Could not detect XDG directories for user")
|
||||
.set_source(Some(std::sync::Arc::new(Box::new(err))))
|
||||
.set_kind(ErrorKind::NotSupported)
|
||||
})?;
|
||||
xdg_dirs
|
||||
.place_config_file("config.toml")
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
"Cannot create configuration directory in {}",
|
||||
xdg_dirs.get_config_home().display()
|
||||
)
|
||||
})
|
||||
.chain_err_kind(ErrorKind::OSError)
|
||||
}
|
||||
|
||||
pub fn get_included_configs(conf_path: PathBuf) -> Result<Vec<PathBuf>> {
|
||||
|
@ -336,7 +336,7 @@ changequote(`"', `"')dnl
|
|||
let mut contents = String::new();
|
||||
while let Some((parent, p)) = stack.pop() {
|
||||
if !p.exists() || p.is_dir() {
|
||||
return Err(format!(
|
||||
return Err(Error::new(format!(
|
||||
"Path {}{included}{in_parent} {msg}.",
|
||||
p.display(),
|
||||
included = if parent.is_some() {
|
||||
|
@ -354,8 +354,8 @@ changequote(`"', `"')dnl
|
|||
} else {
|
||||
"is a directory, not a text file"
|
||||
}
|
||||
)
|
||||
.into());
|
||||
))
|
||||
.set_kind(ErrorKind::Configuration));
|
||||
}
|
||||
contents.clear();
|
||||
let mut file = std::fs::File::open(&p)?;
|
||||
|
@ -368,11 +368,18 @@ changequote(`"', `"')dnl
|
|||
.spawn()
|
||||
{
|
||||
Ok(handle) => handle,
|
||||
Err(error) => match error.kind() {
|
||||
Err(err) => match err.kind() {
|
||||
io::ErrorKind::NotFound => {
|
||||
return Err("`m4` executable not found. Please install.".into())
|
||||
return Err(Error::new(
|
||||
"`m4` executable not found in PATH. Please provide an m4 binary.",
|
||||
)
|
||||
.set_kind(ErrorKind::Platform))
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::new("Could not process configuration with `m4`")
|
||||
.set_source(Some(Arc::new(err)))
|
||||
.set_kind(ErrorKind::OSError))
|
||||
}
|
||||
_ => return Err(error.into()),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -426,7 +433,8 @@ impl FileSettings {
|
|||
if !config_path.exists() {
|
||||
let path_string = config_path.display().to_string();
|
||||
if path_string.is_empty() {
|
||||
return Err(Error::new("No configuration found."));
|
||||
return Err(Error::new("Given configuration path is empty.")
|
||||
.set_kind(ErrorKind::Configuration));
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
let ask = Ask {
|
||||
|
@ -438,14 +446,17 @@ impl FileSettings {
|
|||
#[cfg(not(test))]
|
||||
if ask.run() {
|
||||
create_config_file(&config_path)?;
|
||||
return Err(Error::new(
|
||||
"Edit the sample configuration and relaunch meli.",
|
||||
));
|
||||
return Err(
|
||||
Error::new("Edit the sample configuration and relaunch meli.")
|
||||
.set_kind(ErrorKind::Configuration),
|
||||
);
|
||||
}
|
||||
#[cfg(test)]
|
||||
return Ok(Self::default());
|
||||
#[cfg(not(test))]
|
||||
return Err(Error::new("No configuration file found."));
|
||||
return Err(
|
||||
Error::new("No configuration file found.").set_kind(ErrorKind::Configuration)
|
||||
);
|
||||
}
|
||||
|
||||
Self::validate(config_path, true, false)
|
||||
|
@ -492,14 +503,13 @@ This is required so that you don't accidentally start meli and find out later th
|
|||
"{}\n\nEdit the {} and relaunch meli.",
|
||||
if interactive { "" } else { err_msg },
|
||||
path.display()
|
||||
)));
|
||||
))
|
||||
.set_kind(ErrorKind::Configuration));
|
||||
}
|
||||
let mut s: Self = toml::from_str(&s).map_err(|err| {
|
||||
Error::new(format!(
|
||||
"{}: Config file contains errors; {}",
|
||||
path.display(),
|
||||
err
|
||||
))
|
||||
Error::new(format!("{}: Config file contains errors", path.display()))
|
||||
.set_source(Some(Arc::new(err)))
|
||||
.set_kind(ErrorKind::Configuration)
|
||||
})?;
|
||||
let backends = melib::backends::Backends::new();
|
||||
let Themes {
|
||||
|
@ -525,10 +535,11 @@ This is required so that you don't accidentally start meli and find out later th
|
|||
}
|
||||
}
|
||||
match s.terminal.theme.as_str() {
|
||||
"dark" | "light" => {}
|
||||
themes::DARK | themes::LIGHT => {}
|
||||
t if s.terminal.themes.other_themes.contains_key(t) => {}
|
||||
t => {
|
||||
return Err(Error::new(format!("Theme `{}` was not found.", t)));
|
||||
return Err(Error::new(format!("Theme `{}` was not found.", t))
|
||||
.set_kind(ErrorKind::Configuration));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,7 +587,8 @@ This is required so that you don't accidentally start meli and find out later th
|
|||
return Err(Error::new(format!(
|
||||
"Unrecognised configuration values: {:?}",
|
||||
s.extra
|
||||
)));
|
||||
))
|
||||
.set_kind(ErrorKind::Configuration));
|
||||
}
|
||||
if clear_extras {
|
||||
acc.extra.clear();
|
||||
|
|
|
@ -47,11 +47,14 @@ use crate::{
|
|||
Context,
|
||||
};
|
||||
|
||||
pub const LIGHT: &str = "light";
|
||||
pub const DARK: &str = "dark";
|
||||
|
||||
#[inline(always)]
|
||||
pub fn value(context: &Context, key: &'static str) -> ThemeAttribute {
|
||||
let theme = match context.settings.terminal.theme.as_str() {
|
||||
"light" => &context.settings.terminal.themes.light,
|
||||
"dark" => &context.settings.terminal.themes.dark,
|
||||
self::LIGHT => &context.settings.terminal.themes.light,
|
||||
self::DARK => &context.settings.terminal.themes.dark,
|
||||
t => context
|
||||
.settings
|
||||
.terminal
|
||||
|
@ -66,8 +69,8 @@ pub fn value(context: &Context, key: &'static str) -> ThemeAttribute {
|
|||
#[inline(always)]
|
||||
pub fn fg_color(context: &Context, key: &'static str) -> Color {
|
||||
let theme = match context.settings.terminal.theme.as_str() {
|
||||
"light" => &context.settings.terminal.themes.light,
|
||||
"dark" => &context.settings.terminal.themes.dark,
|
||||
self::LIGHT => &context.settings.terminal.themes.light,
|
||||
self::DARK => &context.settings.terminal.themes.dark,
|
||||
t => context
|
||||
.settings
|
||||
.terminal
|
||||
|
@ -82,8 +85,8 @@ pub fn fg_color(context: &Context, key: &'static str) -> Color {
|
|||
#[inline(always)]
|
||||
pub fn bg_color(context: &Context, key: &'static str) -> Color {
|
||||
let theme = match context.settings.terminal.theme.as_str() {
|
||||
"light" => &context.settings.terminal.themes.light,
|
||||
"dark" => &context.settings.terminal.themes.dark,
|
||||
self::LIGHT => &context.settings.terminal.themes.light,
|
||||
self::DARK => &context.settings.terminal.themes.dark,
|
||||
t => context
|
||||
.settings
|
||||
.terminal
|
||||
|
@ -98,8 +101,8 @@ pub fn bg_color(context: &Context, key: &'static str) -> Color {
|
|||
#[inline(always)]
|
||||
pub fn attrs(context: &Context, key: &'static str) -> Attr {
|
||||
let theme = match context.settings.terminal.theme.as_str() {
|
||||
"light" => &context.settings.terminal.themes.light,
|
||||
"dark" => &context.settings.terminal.themes.dark,
|
||||
self::LIGHT => &context.settings.terminal.themes.light,
|
||||
self::DARK => &context.settings.terminal.themes.dark,
|
||||
t => context
|
||||
.settings
|
||||
.terminal
|
||||
|
@ -318,6 +321,7 @@ const DEFAULT_KEYS: &[&str] = &[
|
|||
"mail.listing.attachment_flag",
|
||||
"mail.listing.thread_snooze_flag",
|
||||
"mail.listing.tag_default",
|
||||
"mail.listing.highlight_self",
|
||||
"pager.highlight_search",
|
||||
"pager.highlight_search_current",
|
||||
];
|
||||
|
@ -648,8 +652,8 @@ mod regexp {
|
|||
key: &'static str,
|
||||
) -> SmallVec<[TextFormatter<'ctx>; 64]> {
|
||||
let theme = match context.settings.terminal.theme.as_str() {
|
||||
"light" => &context.settings.terminal.themes.light,
|
||||
"dark" => &context.settings.terminal.themes.dark,
|
||||
self::LIGHT => &context.settings.terminal.themes.light,
|
||||
self::DARK => &context.settings.terminal.themes.dark,
|
||||
t => context
|
||||
.settings
|
||||
.terminal
|
||||
|
@ -1213,8 +1217,8 @@ impl Themes {
|
|||
}
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
let hash_set: HashSet<&'static str> = DEFAULT_KEYS.iter().copied().collect();
|
||||
Self::validate_keys("light", &self.light, &hash_set)?;
|
||||
Self::validate_keys("dark", &self.dark, &hash_set)?;
|
||||
Self::validate_keys(self::LIGHT, &self.light, &hash_set)?;
|
||||
Self::validate_keys(self::DARK, &self.dark, &hash_set)?;
|
||||
for (name, t) in self.other_themes.iter() {
|
||||
Self::validate_keys(name, t, &hash_set)?;
|
||||
}
|
||||
|
@ -1234,8 +1238,8 @@ impl Themes {
|
|||
|
||||
pub fn key_to_string(&self, key: &str, unlink: bool) -> String {
|
||||
let theme = match key {
|
||||
"light" => &self.light,
|
||||
"dark" => &self.dark,
|
||||
self::LIGHT => &self.light,
|
||||
self::DARK => &self.dark,
|
||||
t => self.other_themes.get(t).unwrap_or(&self.dark),
|
||||
};
|
||||
let mut ret = String::new();
|
||||
|
@ -1270,10 +1274,10 @@ impl Themes {
|
|||
impl std::fmt::Display for Themes {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let mut ret = String::new();
|
||||
ret.push_str(&self.key_to_string("dark", true));
|
||||
ret.push_str(&self.key_to_string(self::DARK, true));
|
||||
|
||||
ret.push_str("\n\n");
|
||||
ret.push_str(&self.key_to_string("light", true));
|
||||
ret.push_str(&self.key_to_string(self::LIGHT, true));
|
||||
for name in self.other_themes.keys() {
|
||||
ret.push_str("\n\n");
|
||||
ret.push_str(&self.key_to_string(name, true));
|
||||
|
@ -1727,6 +1731,15 @@ impl Default for Themes {
|
|||
attrs: Attr::BOLD
|
||||
}
|
||||
);
|
||||
add!(
|
||||
"mail.listing.highlight_self",
|
||||
light = {
|
||||
fg: Color::BLUE,
|
||||
},
|
||||
dark = {
|
||||
fg: Color::BLUE,
|
||||
}
|
||||
);
|
||||
|
||||
add!("pager.highlight_search", light = { fg: Color::White, bg: Color::Byte(6) /* Teal */, attrs: Attr::BOLD }, dark = { fg: Color::White, bg: Color::Byte(6) /* Teal */, attrs: Attr::BOLD });
|
||||
add!("pager.highlight_search_current", light = { fg: Color::White, bg: Color::Byte(17) /* NavyBlue */, attrs: Attr::BOLD }, dark = { fg: Color::White, bg: Color::Byte(17) /* NavyBlue */, attrs: Attr::BOLD });
|
||||
|
@ -1803,8 +1816,8 @@ impl Serialize for Themes {
|
|||
other_themes.insert(name.to_string(), new_map);
|
||||
}
|
||||
|
||||
other_themes.insert("light".to_string(), light);
|
||||
other_themes.insert("dark".to_string(), dark);
|
||||
other_themes.insert(self::LIGHT.to_string(), light);
|
||||
other_themes.insert(self::DARK.to_string(), dark);
|
||||
other_themes.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -268,6 +268,7 @@ pub struct ColorCache {
|
|||
pub even_highlighted_selected: ThemeAttribute,
|
||||
pub odd_highlighted_selected: ThemeAttribute,
|
||||
pub tag_default: ThemeAttribute,
|
||||
pub highlight_self: ThemeAttribute,
|
||||
|
||||
// Conversations
|
||||
pub subject: ThemeAttribute,
|
||||
|
@ -277,6 +278,12 @@ pub struct ColorCache {
|
|||
|
||||
impl ColorCache {
|
||||
pub fn new(context: &Context, style: IndexStyle) -> Self {
|
||||
let default = Self {
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
highlight_self: crate::conf::value(context, "mail.listing.highlight_self"),
|
||||
..Self::default()
|
||||
};
|
||||
let mut ret = match style {
|
||||
IndexStyle::Plain => Self {
|
||||
even: crate::conf::value(context, "mail.listing.plain.even"),
|
||||
|
@ -298,9 +305,7 @@ impl ColorCache {
|
|||
"mail.listing.plain.even_highlighted_selected",
|
||||
),
|
||||
odd_selected: crate::conf::value(context, "mail.listing.plain.odd_selected"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..Self::default()
|
||||
..default
|
||||
},
|
||||
IndexStyle::Threaded => Self {
|
||||
even_unseen: crate::conf::value(context, "mail.listing.plain.even_unseen"),
|
||||
|
@ -322,9 +327,7 @@ impl ColorCache {
|
|||
),
|
||||
even: crate::conf::value(context, "mail.listing.plain.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.plain.odd"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..Self::default()
|
||||
..default
|
||||
},
|
||||
IndexStyle::Compact => Self {
|
||||
even_unseen: crate::conf::value(context, "mail.listing.compact.even_unseen"),
|
||||
|
@ -349,12 +352,9 @@ impl ColorCache {
|
|||
),
|
||||
even: crate::conf::value(context, "mail.listing.compact.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.compact.odd"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..Self::default()
|
||||
..default
|
||||
},
|
||||
IndexStyle::Conversations => Self {
|
||||
theme_default: crate::conf::value(context, "mail.listing.conversations"),
|
||||
subject: crate::conf::value(context, "mail.listing.conversations.subject"),
|
||||
from: crate::conf::value(context, "mail.listing.conversations.from"),
|
||||
date: crate::conf::value(context, "mail.listing.conversations.date"),
|
||||
|
@ -365,13 +365,13 @@ impl ColorCache {
|
|||
context,
|
||||
"mail.listing.conversations.highlighted_selected",
|
||||
),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
..Self::default()
|
||||
..default
|
||||
},
|
||||
};
|
||||
if !context.settings.terminal.use_color() {
|
||||
ret.highlighted.attrs |= Attr::REVERSE;
|
||||
ret.tag_default.attrs |= Attr::REVERSE;
|
||||
ret.highlight_self.attrs |= Attr::REVERSE;
|
||||
ret.even_highlighted.attrs |= Attr::REVERSE;
|
||||
ret.odd_highlighted.attrs |= Attr::REVERSE;
|
||||
ret.even_highlighted_selected.attrs |= Attr::REVERSE | Attr::DIM;
|
||||
|
@ -388,6 +388,7 @@ pub struct EntryStrings {
|
|||
pub flag: FlagString,
|
||||
pub from: FromString,
|
||||
pub tags: TagString,
|
||||
pub unseen: bool,
|
||||
pub highlight_self: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ pub struct CompactListing {
|
|||
sortcmd: bool,
|
||||
subsort: (SortField, SortOrder),
|
||||
/// Cache current view.
|
||||
data_columns: DataColumns<4>,
|
||||
data_columns: DataColumns<5>,
|
||||
rows_drawn: SegmentTree,
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
|
||||
|
@ -234,13 +234,13 @@ impl MailListingTrait for CompactListing {
|
|||
let message: String =
|
||||
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
|
||||
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
|
||||
let area = self.data_columns.columns[0].area();
|
||||
let area_col_0 = self.data_columns.columns[0].area();
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
message.as_str(),
|
||||
self.color_cache.theme_default.fg,
|
||||
self.color_cache.theme_default.bg,
|
||||
self.color_cache.theme_default.attrs,
|
||||
area,
|
||||
area_col_0,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
@ -299,18 +299,20 @@ impl MailListingTrait for CompactListing {
|
|||
self.sort = context.accounts[&self.cursor_pos.0].settings.account.order
|
||||
}
|
||||
self.length = 0;
|
||||
let mut min_width = (0, 0, 0, 0);
|
||||
let mut min_width = (0, 0, 0, 0, 0);
|
||||
#[allow(clippy::type_complexity)]
|
||||
let mut row_widths: (
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
) = (
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
);
|
||||
|
||||
let tags_lck = account.collection.tag_index.read().unwrap();
|
||||
|
@ -325,6 +327,12 @@ impl MailListingTrait for CompactListing {
|
|||
.settings
|
||||
.account
|
||||
.make_display_name();
|
||||
let should_highlight_self = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true();
|
||||
'items_for_loop: for thread in items {
|
||||
let thread_node = &threads.thread_nodes()[&threads.thread_ref(thread).root()];
|
||||
let root_env_hash = if let Some(h) = thread_node.message().or_else(|| {
|
||||
|
@ -401,11 +409,7 @@ impl MailListingTrait for CompactListing {
|
|||
}
|
||||
}
|
||||
|
||||
highlight_self |= envelope
|
||||
.to()
|
||||
.iter()
|
||||
.chain(envelope.cc().iter())
|
||||
.any(|a| a == &my_address);
|
||||
highlight_self |= should_highlight_self && envelope.recipient_any(&my_address);
|
||||
for addr in envelope.from().iter() {
|
||||
if from_address_set.contains(addr.address_spec_raw()) {
|
||||
continue;
|
||||
|
@ -414,15 +418,6 @@ impl MailListingTrait for CompactListing {
|
|||
from_address_list.push(addr.clone());
|
||||
}
|
||||
}
|
||||
if !mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true()
|
||||
{
|
||||
highlight_self = false;
|
||||
}
|
||||
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
|
@ -463,22 +458,25 @@ impl MailListingTrait for CompactListing {
|
|||
.try_into()
|
||||
.unwrap_or(255),
|
||||
);
|
||||
/* subject */
|
||||
row_widths.3.push(
|
||||
(entry_strings.flag.grapheme_width()
|
||||
+ entry_strings.subject.grapheme_width()
|
||||
+ 1
|
||||
+ entry_strings.tags.grapheme_width())
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
entry_strings
|
||||
.flag
|
||||
.grapheme_width()
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
);
|
||||
row_widths.4.push(
|
||||
(entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width())
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
);
|
||||
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
|
||||
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
|
||||
min_width.3 = min_width.3.max(
|
||||
entry_strings.flag.grapheme_width()
|
||||
+ entry_strings.subject.grapheme_width()
|
||||
+ 1
|
||||
+ entry_strings.tags.grapheme_width(),
|
||||
entry_strings.flag.grapheme_width() + usize::from(entry_strings.highlight_self),
|
||||
);
|
||||
min_width.4 = min_width.4.max(
|
||||
entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width(),
|
||||
); /* subject */
|
||||
self.rows.insert_thread(
|
||||
thread,
|
||||
|
@ -494,12 +492,13 @@ impl MailListingTrait for CompactListing {
|
|||
self.length += 1;
|
||||
}
|
||||
|
||||
min_width.0 = self.length.saturating_sub(1).to_string().len();
|
||||
min_width.0 = digits_of_num!(self.length.saturating_sub(1));
|
||||
|
||||
self.data_columns.elasticities[0].set_rigid();
|
||||
self.data_columns.elasticities[1].set_rigid();
|
||||
self.data_columns.elasticities[2].set_grow(15, Some(35));
|
||||
self.data_columns.elasticities[3].set_rigid();
|
||||
self.data_columns.elasticities[4].set_rigid();
|
||||
self.data_columns
|
||||
.cursor_config
|
||||
.set_handle(true)
|
||||
|
@ -517,12 +516,15 @@ impl MailListingTrait for CompactListing {
|
|||
_ = self.data_columns.columns[1].resize_with_context(min_width.1, self.rows.len(), context);
|
||||
/* from column */
|
||||
_ = self.data_columns.columns[2].resize_with_context(min_width.2, self.rows.len(), context);
|
||||
/* subject column */
|
||||
// flags column
|
||||
_ = self.data_columns.columns[3].resize_with_context(min_width.3, self.rows.len(), context);
|
||||
// subject column
|
||||
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
|
||||
self.data_columns.segment_tree[0] = row_widths.0.into();
|
||||
self.data_columns.segment_tree[1] = row_widths.1.into();
|
||||
self.data_columns.segment_tree[2] = row_widths.2.into();
|
||||
self.data_columns.segment_tree[3] = row_widths.3.into();
|
||||
self.data_columns.segment_tree[4] = row_widths.4.into();
|
||||
|
||||
self.rows_drawn = SegmentTree::from(
|
||||
std::iter::repeat(1)
|
||||
|
@ -534,13 +536,13 @@ impl MailListingTrait for CompactListing {
|
|||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
let message: String = account[&self.cursor_pos.1].status();
|
||||
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
|
||||
let area = self.data_columns.columns[0].area();
|
||||
let area_col_0 = self.data_columns.columns[0].area();
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
&message,
|
||||
self.color_cache.theme_default.fg,
|
||||
self.color_cache.theme_default.bg,
|
||||
self.color_cache.theme_default.attrs,
|
||||
area,
|
||||
area_col_0,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
@ -688,6 +690,10 @@ impl ListingTrait for CompactListing {
|
|||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx, context);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
if !self.force_draw {
|
||||
return;
|
||||
}
|
||||
|
@ -707,6 +713,9 @@ impl ListingTrait for CompactListing {
|
|||
/* copy table columns */
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx, context);
|
||||
}
|
||||
/* apply each row colors separately */
|
||||
for i in top_idx..(top_idx + area.height()) {
|
||||
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
|
||||
|
@ -987,6 +996,7 @@ impl CompactListing {
|
|||
),
|
||||
from: FromString(Address::display_name_slice(from)),
|
||||
tags: TagString(tags_string, colors),
|
||||
unseen: thread.unseen() > 0,
|
||||
highlight_self,
|
||||
}
|
||||
}
|
||||
|
@ -1033,6 +1043,12 @@ impl CompactListing {
|
|||
let mut from_address_set: std::collections::HashSet<Vec<u8>> =
|
||||
std::collections::HashSet::new();
|
||||
let mut highlight_self: bool = false;
|
||||
let should_highlight_self = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true();
|
||||
let my_address: Address = context.accounts[&self.cursor_pos.0]
|
||||
.settings
|
||||
.account
|
||||
|
@ -1061,11 +1077,7 @@ impl CompactListing {
|
|||
tags.insert(t);
|
||||
}
|
||||
}
|
||||
highlight_self |= envelope
|
||||
.to()
|
||||
.iter()
|
||||
.chain(envelope.cc().iter())
|
||||
.any(|a| a == &my_address);
|
||||
highlight_self |= should_highlight_self && envelope.recipient_any(&my_address);
|
||||
for addr in envelope.from().iter() {
|
||||
if from_address_set.contains(addr.address_spec_raw()) {
|
||||
continue;
|
||||
|
@ -1074,17 +1086,8 @@ impl CompactListing {
|
|||
from_address_list.push(addr.clone());
|
||||
}
|
||||
}
|
||||
if !mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true()
|
||||
{
|
||||
highlight_self = false;
|
||||
}
|
||||
|
||||
let strings = self.make_entry_string(
|
||||
let mut entry_strings = self.make_entry_string(
|
||||
&envelope,
|
||||
context,
|
||||
&tags_lck,
|
||||
|
@ -1095,161 +1098,22 @@ impl CompactListing {
|
|||
highlight_self,
|
||||
thread_hash,
|
||||
);
|
||||
entry_strings.highlight_self = should_highlight_self && {
|
||||
let my_address: Address = context.accounts[&self.cursor_pos.0]
|
||||
.settings
|
||||
.account
|
||||
.make_display_name();
|
||||
envelope.recipient_any(&my_address)
|
||||
};
|
||||
drop(envelope);
|
||||
let columns = &mut self.data_columns.columns;
|
||||
let min_width = (
|
||||
columns[0].area().width(),
|
||||
columns[1].area().width(),
|
||||
columns[2].area().width(),
|
||||
columns[3].area().width(),
|
||||
);
|
||||
let (x, _) = {
|
||||
let area = columns[0].area().nth_row(idx);
|
||||
columns[0].grid_mut().write_string(
|
||||
&idx.to_string(),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[0].area();
|
||||
columns[0].grid_mut().row_iter(area, x..min_width.0, idx)
|
||||
} {
|
||||
columns[0].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs)
|
||||
.set_ch(' ');
|
||||
for n in 0..=4 {
|
||||
let area = columns[n].area().nth_row(idx);
|
||||
columns[n].grid_mut().clear_area(area, row_attr);
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[1].area().nth_row(idx);
|
||||
columns[1].grid_mut().write_string(
|
||||
&strings.date,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[1].area();
|
||||
columns[1].grid_mut().row_iter(area, x..min_width.1, idx)
|
||||
} {
|
||||
columns[1].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs)
|
||||
.set_ch(' ');
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[2].area().nth_row(idx);
|
||||
columns[2].grid_mut().write_string(
|
||||
&strings.from,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[2].area();
|
||||
columns[2].grid_mut().row_iter(area, x..min_width.2, idx)
|
||||
} {
|
||||
columns[2].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs)
|
||||
.set_ch(' ');
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx);
|
||||
columns[3].grid_mut().write_string(
|
||||
&strings.flag,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x);
|
||||
columns[3].grid_mut().write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
if let Some(c) = columns[3].grid_mut().get_mut(x, idx) {
|
||||
c.set_bg(row_attr.bg).set_attrs(row_attr.attrs).set_ch(' ');
|
||||
}
|
||||
let x = {
|
||||
let mut x = x + 1;
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let _x = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
|
||||
columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
.0
|
||||
+ x
|
||||
+ 1
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_keep_bg(true);
|
||||
}
|
||||
x = _x + 2;
|
||||
}
|
||||
x
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..min_width.3, idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_ch(' ')
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
|
||||
self.rows_drawn.update(idx, 1);
|
||||
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), entry_strings);
|
||||
}
|
||||
|
||||
fn draw_rows(&mut self, context: &Context, start: usize, end: usize) {
|
||||
|
@ -1268,6 +1132,7 @@ impl CompactListing {
|
|||
self.data_columns.columns[1].area().width(),
|
||||
self.data_columns.columns[2].area().width(),
|
||||
self.data_columns.columns[3].area().width(),
|
||||
self.data_columns.columns[3].area().width(),
|
||||
);
|
||||
|
||||
let columns = &mut self.data_columns.columns;
|
||||
|
@ -1358,128 +1223,105 @@ impl CompactListing {
|
|||
}
|
||||
}
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx);
|
||||
columns[3].grid_mut().write_string(
|
||||
{
|
||||
let mut area_col_3 = columns[3].area().nth_row(idx);
|
||||
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
|
||||
&strings.flag,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_3,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let x = {
|
||||
let mut area = columns[3].area().nth_row(idx).skip_cols(x);
|
||||
));
|
||||
if strings.highlight_self {
|
||||
// [ref:hardcoded_color_value]: add highlight_self theme attr
|
||||
let x = columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
|
||||
Color::BLUE,
|
||||
row_attr.bg,
|
||||
row_attr.attrs | Attr::FORCE_TEXT,
|
||||
area,
|
||||
None,
|
||||
let (x, _) = columns[3].grid_mut().write_string(
|
||||
mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self_flag
|
||||
)
|
||||
.0;
|
||||
for row in columns[3].grid().bounds_iter(area.nth_row(0).take_cols(x)) {
|
||||
for c in row {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true);
|
||||
}
|
||||
}
|
||||
area = area.skip_cols(x + 1);
|
||||
}
|
||||
columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
|
||||
self.color_cache.highlight_self.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
row_attr.attrs | Attr::FORCE_TEXT,
|
||||
area_col_3,
|
||||
None,
|
||||
)
|
||||
.0
|
||||
+ x
|
||||
};
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in crate::conf::text_format_regexps(context, "listing.subject") {
|
||||
let t = columns[3].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in text_formatter.regexp.find_iter(strings.subject.as_str()) {
|
||||
columns[3].grid_mut().set_tag(t, (start, idx), (end, idx));
|
||||
);
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true);
|
||||
}
|
||||
area_col_3 = area_col_3.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
let mut x = x + 1;
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let _x = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
|
||||
columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
.0
|
||||
+ x
|
||||
+ 1
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color);
|
||||
{
|
||||
let mut area_col_4 = columns[4].area().nth_row(idx);
|
||||
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area_col_4,
|
||||
None,
|
||||
));
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in
|
||||
crate::conf::text_format_regexps(context, "listing.subject")
|
||||
{
|
||||
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in
|
||||
text_formatter.regexp.find_iter(strings.subject.as_str())
|
||||
{
|
||||
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
|
||||
area_col_4 = area_col_4.skip_cols(1);
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let (x, _) = columns[4].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area_col_4,
|
||||
None,
|
||||
);
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_bg(color)
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
area_col_4 = area_col_4.skip_cols(x + 1);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_ch(' ')
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_keep_bg(true);
|
||||
}
|
||||
x = _x + 2;
|
||||
}
|
||||
}
|
||||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
let message: String = account[&self.cursor_pos.1].status();
|
||||
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
|
||||
let area = self.data_columns.columns[0].area();
|
||||
let area_col_0 = self.data_columns.columns[0].area();
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
message.as_str(),
|
||||
self.color_cache.theme_default.fg,
|
||||
self.color_cache.theme_default.bg,
|
||||
self.color_cache.theme_default.attrs,
|
||||
area,
|
||||
area_col_0,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
@ -1532,6 +1374,53 @@ impl CompactListing {
|
|||
}
|
||||
}
|
||||
|
||||
fn draw_relative_numbers(
|
||||
&mut self,
|
||||
grid: &mut CellBuffer,
|
||||
area: Area,
|
||||
top_idx: usize,
|
||||
context: &Context,
|
||||
) {
|
||||
let width = self.data_columns.columns[0].area().width();
|
||||
let area = area.take_cols(width);
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||
for i in 0..area.height() {
|
||||
let idx = top_idx + i;
|
||||
if idx >= self.length {
|
||||
break;
|
||||
}
|
||||
let row_attr = if let Some(thread_hash) = self.get_thread_under_cursor(idx) {
|
||||
let thread = threads.thread_ref(thread_hash);
|
||||
row_attr!(
|
||||
self.color_cache,
|
||||
even: idx % 2 == 0,
|
||||
unseen: thread.unseen() > 0,
|
||||
highlighted: self.new_cursor_pos.2 == idx,
|
||||
selected: self.rows.is_thread_selected(thread_hash)
|
||||
)
|
||||
} else {
|
||||
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
|
||||
};
|
||||
|
||||
grid.clear_area(area.nth_row(i), row_attr);
|
||||
grid.write_string(
|
||||
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
|
||||
self.new_cursor_pos.2.to_string()
|
||||
} else {
|
||||
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
|
||||
.abs()
|
||||
.to_string()
|
||||
},
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(i),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_movement(&mut self, height: Option<usize>) {
|
||||
let rows = height.unwrap_or(1);
|
||||
if let Some(mvm) = self.movement.take() {
|
||||
|
|
|
@ -771,6 +771,7 @@ impl ConversationsListing {
|
|||
),
|
||||
from: FromString(Address::display_name_slice(from)),
|
||||
tags: TagString(tags_string, colors),
|
||||
unseen: thread.unseen() > 0,
|
||||
highlight_self: false,
|
||||
}
|
||||
}
|
||||
|
@ -877,10 +878,10 @@ impl ConversationsListing {
|
|||
None,
|
||||
);
|
||||
if !strings.flag.is_empty() {
|
||||
for c in grid.row_iter(area, x..(x + 3), 0) {
|
||||
for c in grid.row_iter(area, x..(x + 1), 0) {
|
||||
grid[c].set_bg(row_attr.bg);
|
||||
}
|
||||
x += 3;
|
||||
x += 1;
|
||||
}
|
||||
let subject_attr = row_attr!(
|
||||
subject,
|
||||
|
|
|
@ -137,7 +137,7 @@ pub struct PlainListing {
|
|||
subsort: (SortField, SortOrder),
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
/// Cache current view.
|
||||
data_columns: DataColumns<4>,
|
||||
data_columns: DataColumns<5>,
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||
|
@ -442,6 +442,10 @@ impl ListingTrait for PlainListing {
|
|||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
if !self.force_draw {
|
||||
return;
|
||||
}
|
||||
|
@ -461,6 +465,9 @@ impl ListingTrait for PlainListing {
|
|||
/* copy table columns */
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx);
|
||||
}
|
||||
/* apply each row colors separately */
|
||||
for i in top_idx..(top_idx + area.height()) {
|
||||
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
|
||||
|
@ -696,6 +703,7 @@ impl PlainListing {
|
|||
),
|
||||
from: FromString(Address::display_name_slice(e.from())),
|
||||
tags: TagString(tags, colors),
|
||||
unseen: !e.is_seen(),
|
||||
highlight_self: false,
|
||||
}
|
||||
}
|
||||
|
@ -714,15 +722,28 @@ impl PlainListing {
|
|||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
SmallVec<[u8; 1024]>,
|
||||
) = (
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
SmallVec::new(),
|
||||
);
|
||||
|
||||
let should_highlight_self = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true();
|
||||
let my_address: Address = context.accounts[&self.cursor_pos.0]
|
||||
.settings
|
||||
.account
|
||||
.make_display_name();
|
||||
for i in iter {
|
||||
if !context.accounts[&self.cursor_pos.0].contains_key(i) {
|
||||
if !context.accounts[&self.cursor_pos.0].contains_key(i)
|
||||
|| !threads.envelope_to_thread.contains_key(&i)
|
||||
{
|
||||
log::debug!("key = {}", i);
|
||||
log::debug!(
|
||||
"name = {} {}",
|
||||
|
@ -755,7 +776,9 @@ impl PlainListing {
|
|||
);
|
||||
self.rows.row_attr_cache.insert(self.length, row_attr);
|
||||
|
||||
let entry_strings = self.make_entry_string(&envelope, context);
|
||||
let mut entry_strings = self.make_entry_string(&envelope, context);
|
||||
entry_strings.highlight_self =
|
||||
should_highlight_self && { envelope.recipient_any(&my_address) };
|
||||
row_widths
|
||||
.0
|
||||
.push(digits_of_num!(self.length).try_into().unwrap_or(255));
|
||||
|
@ -774,20 +797,22 @@ impl PlainListing {
|
|||
.unwrap_or(255),
|
||||
);
|
||||
row_widths.3.push(
|
||||
(entry_strings.flag.grapheme_width()
|
||||
+ entry_strings.subject.grapheme_width()
|
||||
+ 1
|
||||
+ entry_strings.tags.grapheme_width())
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
entry_strings
|
||||
.flag
|
||||
.grapheme_width()
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
); /* flags */
|
||||
row_widths.4.push(
|
||||
(entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width())
|
||||
.try_into()
|
||||
.unwrap_or(255),
|
||||
);
|
||||
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
|
||||
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
|
||||
min_width.3 = min_width.3.max(
|
||||
entry_strings.flag.grapheme_width()
|
||||
+ entry_strings.subject.grapheme_width()
|
||||
+ 1
|
||||
+ entry_strings.tags.grapheme_width(),
|
||||
min_width.3 = min_width.3.max(entry_strings.flag.grapheme_width()); /* flags */
|
||||
min_width.4 = min_width.4.max(
|
||||
entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width(),
|
||||
); /* tags + subject */
|
||||
self.rows.insert_thread(
|
||||
threads.envelope_to_thread[&i],
|
||||
|
@ -805,6 +830,7 @@ impl PlainListing {
|
|||
self.data_columns.elasticities[1].set_rigid();
|
||||
self.data_columns.elasticities[2].set_grow(15, Some(35));
|
||||
self.data_columns.elasticities[3].set_rigid();
|
||||
self.data_columns.elasticities[4].set_rigid();
|
||||
self.data_columns
|
||||
.cursor_config
|
||||
.set_handle(true)
|
||||
|
@ -822,12 +848,15 @@ impl PlainListing {
|
|||
_ = self.data_columns.columns[1].resize_with_context(min_width.1, self.rows.len(), context);
|
||||
/* from column */
|
||||
_ = self.data_columns.columns[2].resize_with_context(min_width.2, self.rows.len(), context);
|
||||
/* subject column */
|
||||
// flags column
|
||||
_ = self.data_columns.columns[3].resize_with_context(min_width.3, self.rows.len(), context);
|
||||
// subject column
|
||||
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
|
||||
self.data_columns.segment_tree[0] = row_widths.0.into();
|
||||
self.data_columns.segment_tree[1] = row_widths.1.into();
|
||||
self.data_columns.segment_tree[2] = row_widths.2.into();
|
||||
self.data_columns.segment_tree[3] = row_widths.3.into();
|
||||
self.data_columns.segment_tree[4] = row_widths.4.into();
|
||||
|
||||
let iter = if self.filter_term.is_empty() {
|
||||
Box::new(self.local_collection.iter().cloned())
|
||||
|
@ -853,137 +882,159 @@ impl PlainListing {
|
|||
|
||||
let row_attr = self.rows.row_attr_cache[&idx];
|
||||
|
||||
let (x, _) = {
|
||||
let area = columns[0].area().nth_row(idx);
|
||||
columns[0].grid_mut().write_string(
|
||||
&idx.to_string(),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[0].area();
|
||||
columns[0].grid_mut().row_iter(area, x..min_width.0, idx)
|
||||
} {
|
||||
columns[0].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
{
|
||||
let mut area_col_0 = columns[0].area().nth_row(idx);
|
||||
if !*account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
area_col_0 = area_col_0.skip_cols(columns[0].grid_mut().write_string(
|
||||
&idx.to_string(),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area_col_0,
|
||||
None,
|
||||
));
|
||||
for c in columns[0].grid().row_iter(area_col_0, 0..min_width.0, 0) {
|
||||
columns[0].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[1].area().nth_row(idx);
|
||||
columns[1].grid_mut().write_string(
|
||||
{
|
||||
let mut area_col_1 = columns[1].area().nth_row(idx);
|
||||
area_col_1 = area_col_1.skip_cols(columns[1].grid_mut().write_string(
|
||||
&strings.date,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_1,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[1].area();
|
||||
columns[1].grid_mut().row_iter(area, x..min_width.1, idx)
|
||||
} {
|
||||
columns[1].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
));
|
||||
for c in columns[1].grid().row_iter(area_col_1, 0..min_width.1, 0) {
|
||||
columns[1].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[2].area().nth_row(idx);
|
||||
columns[2].grid_mut().write_string(
|
||||
{
|
||||
let area_col_2 = columns[2].area().nth_row(idx);
|
||||
let (skip_cols, _) = columns[2].grid_mut().write_string(
|
||||
&strings.from,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_2,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[2].area();
|
||||
columns[2].grid_mut().row_iter(area, x..min_width.2, idx)
|
||||
} {
|
||||
columns[2].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs)
|
||||
.set_ch(' ');
|
||||
);
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in crate::conf::text_format_regexps(context, "listing.from")
|
||||
{
|
||||
let t = columns[2].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in text_formatter.regexp.find_iter(strings.from.as_str()) {
|
||||
columns[2].grid_mut().set_tag(
|
||||
t,
|
||||
(start + skip_cols, idx),
|
||||
(end + skip_cols, idx),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for c in columns[2]
|
||||
.grid()
|
||||
.row_iter(area_col_2, skip_cols..min_width.2, 0)
|
||||
{
|
||||
columns[2].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx);
|
||||
columns[3].grid_mut().write_string(
|
||||
{
|
||||
let mut area_col_3 = columns[3].area().nth_row(idx);
|
||||
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
|
||||
&strings.flag,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_3,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let x = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x);
|
||||
columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
.0
|
||||
+ x
|
||||
};
|
||||
let mut x = x + 1;
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let _x = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
|
||||
columns[3]
|
||||
.grid_mut()
|
||||
.write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area,
|
||||
None,
|
||||
));
|
||||
if strings.highlight_self {
|
||||
let (x, _) = columns[3].grid_mut().write_string(
|
||||
mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self_flag
|
||||
)
|
||||
.0
|
||||
+ x
|
||||
+ 1
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color);
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
|
||||
self.color_cache.highlight_self.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs | Attr::FORCE_TEXT,
|
||||
area_col_3,
|
||||
None,
|
||||
);
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true);
|
||||
}
|
||||
area_col_3 = area_col_3.skip_cols(x + 1);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
|
||||
} {
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_keep_bg(true);
|
||||
}
|
||||
{
|
||||
let mut area_col_4 = columns[4].area().nth_row(idx);
|
||||
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area_col_4,
|
||||
None,
|
||||
));
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in
|
||||
crate::conf::text_format_regexps(context, "listing.subject")
|
||||
{
|
||||
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in
|
||||
text_formatter.regexp.find_iter(strings.subject.as_str())
|
||||
{
|
||||
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
area_col_4 = area_col_4.skip_cols(1);
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let (x, _) = columns[4].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area_col_4.skip_cols(1),
|
||||
None,
|
||||
);
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_bg(color)
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
area_col_4 = area_col_4.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_ch(' ')
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
x = _x + 2;
|
||||
}
|
||||
}
|
||||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
|
@ -1033,21 +1084,9 @@ impl PlainListing {
|
|||
let strings = self.make_entry_string(&envelope, context);
|
||||
drop(envelope);
|
||||
let columns = &mut self.data_columns.columns;
|
||||
{
|
||||
let area = columns[0].area().nth_row(idx);
|
||||
columns[0].grid_mut().clear_area(area, row_attr)
|
||||
};
|
||||
{
|
||||
let area = columns[1].area().nth_row(idx);
|
||||
columns[1].grid_mut().clear_area(area, row_attr);
|
||||
}
|
||||
{
|
||||
let area = columns[2].area().nth_row(idx);
|
||||
columns[2].grid_mut().clear_area(area, row_attr);
|
||||
}
|
||||
{
|
||||
let area = columns[3].area().nth_row(idx);
|
||||
columns[3].grid_mut().clear_area(area, row_attr);
|
||||
for n in 0..=4 {
|
||||
let area = columns[n].area().nth_row(idx);
|
||||
columns[n].grid_mut().clear_area(area, row_attr);
|
||||
}
|
||||
|
||||
let (x, _) = {
|
||||
|
@ -1062,8 +1101,8 @@ impl PlainListing {
|
|||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[0].area();
|
||||
columns[0].grid_mut().row_iter(area, x..area.width(), idx)
|
||||
let area = columns[0].area().nth_row(idx);
|
||||
columns[0].grid_mut().row_iter(area, x..area.width(), 0)
|
||||
} {
|
||||
columns[0].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
|
@ -1081,8 +1120,8 @@ impl PlainListing {
|
|||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[1].area();
|
||||
columns[1].grid_mut().row_iter(area, x..area.width(), idx)
|
||||
let area = columns[1].area().nth_row(idx);
|
||||
columns[1].grid_mut().row_iter(area, x..area.width(), 0)
|
||||
} {
|
||||
columns[1].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
|
@ -1100,85 +1139,101 @@ impl PlainListing {
|
|||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[2].area();
|
||||
columns[2].grid_mut().row_iter(area, x..area.width(), idx)
|
||||
let area = columns[2].area().nth_row(idx);
|
||||
columns[2].grid_mut().row_iter(area, x..area.width(), 0)
|
||||
} {
|
||||
columns[2].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx);
|
||||
columns[3].grid_mut().write_string(
|
||||
{
|
||||
let mut area_col_3 = columns[3].area().nth_row(idx);
|
||||
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
|
||||
&strings.flag,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_3,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let (x, _) = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x);
|
||||
columns[3].grid_mut().write_string(
|
||||
));
|
||||
if strings.highlight_self {
|
||||
let (x, _) = columns[3].grid_mut().write_string(
|
||||
mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
|
||||
self.color_cache.highlight_self.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs | Attr::FORCE_TEXT,
|
||||
area_col_3,
|
||||
None,
|
||||
);
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true);
|
||||
}
|
||||
area_col_3 = area_col_3.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[3]
|
||||
.grid()
|
||||
.row_iter(area_col_3, 0..area_col_3.width(), 0)
|
||||
{
|
||||
columns[3].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut area_col_4 = columns[4].area().nth_row(idx);
|
||||
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area,
|
||||
area_col_4,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let x = {
|
||||
let mut x = x + 1;
|
||||
));
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in crate::conf::text_format_regexps(context, "listing.subject") {
|
||||
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in text_formatter.regexp.find_iter(strings.subject.as_str()) {
|
||||
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
area_col_4 = area_col_4.skip_cols(1);
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let (_x, _) = {
|
||||
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
|
||||
columns[3].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area,
|
||||
None,
|
||||
)
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color);
|
||||
let (x, _) = columns[4].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area_col_4.skip_cols(1),
|
||||
None,
|
||||
);
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_bg(color)
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true).set_keep_bg(true);
|
||||
}
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c].set_keep_bg(true);
|
||||
}
|
||||
x = _x + 2;
|
||||
area_col_4 = area_col_4.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[4]
|
||||
.grid()
|
||||
.row_iter(area_col_4, 0..area_col_4.width(), 0)
|
||||
{
|
||||
columns[4].grid_mut()[c]
|
||||
.set_ch(' ')
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
x
|
||||
};
|
||||
for c in {
|
||||
let area = columns[3].area();
|
||||
columns[3].grid().row_iter(area, x..area.width(), idx)
|
||||
} {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
|
||||
}
|
||||
|
@ -1229,6 +1284,49 @@ impl PlainListing {
|
|||
}
|
||||
}
|
||||
|
||||
fn draw_relative_numbers(&mut self, grid: &mut CellBuffer, area: Area, top_idx: usize) {
|
||||
let width = self.data_columns.columns[0].area().width();
|
||||
let area = area.take_cols(width);
|
||||
for i in 0..area.height() {
|
||||
if top_idx + i >= self.length {
|
||||
break;
|
||||
}
|
||||
let row_attr = if let Some(env_hash) = self.get_env_under_cursor(top_idx + i) {
|
||||
let unseen = self
|
||||
.rows
|
||||
.entries
|
||||
.get(top_idx + i)
|
||||
.map(|((_, _), strings)| strings.unseen)
|
||||
.unwrap_or(false);
|
||||
row_attr!(
|
||||
self.color_cache,
|
||||
even: (top_idx + i) % 2 == 0,
|
||||
unseen: unseen,
|
||||
highlighted: self.cursor_pos.2 == (top_idx + i),
|
||||
selected: self.rows.selection[&env_hash]
|
||||
)
|
||||
} else {
|
||||
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
|
||||
};
|
||||
|
||||
grid.clear_area(area.nth_row(i), row_attr);
|
||||
grid.write_string(
|
||||
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
|
||||
self.new_cursor_pos.2.to_string()
|
||||
} else {
|
||||
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
|
||||
.abs()
|
||||
.to_string()
|
||||
},
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(i),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_movement(&mut self, height: Option<usize>) {
|
||||
let rows = height.unwrap_or(1);
|
||||
if let Some(mvm) = self.movement.take() {
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{cmp, convert::TryInto, iter::FromIterator};
|
||||
use std::{convert::TryInto, iter::FromIterator};
|
||||
|
||||
use melib::{Address, SortField, SortOrder, ThreadNode, Threads};
|
||||
|
||||
use super::*;
|
||||
use crate::{components::PageMovement, jobs::JoinHandle};
|
||||
use crate::{components::PageMovement, jobs::JoinHandle, segment_tree::SegmentTree};
|
||||
|
||||
macro_rules! row_attr {
|
||||
($color_cache:expr, even: $even:expr, unseen: $unseen:expr, highlighted: $highlighted:expr, selected: $selected:expr $(,)*) => {{
|
||||
|
@ -146,6 +146,7 @@ pub struct ThreadListing {
|
|||
filtered_selection: Vec<EnvelopeHash>,
|
||||
_filtered_order: HashMap<EnvelopeHash, usize>,
|
||||
data_columns: DataColumns<5>,
|
||||
rows_drawn: SegmentTree,
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
seen_cache: IndexMap<EnvelopeHash, bool>,
|
||||
/// If we must redraw on next redraw event
|
||||
|
@ -300,6 +301,16 @@ impl MailListingTrait for ThreadListing {
|
|||
.listing
|
||||
.threaded_repeat_identical_from_values
|
||||
);
|
||||
let should_highlight_self = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true();
|
||||
let my_address: Address = context.accounts[&self.cursor_pos.0]
|
||||
.settings
|
||||
.account
|
||||
.make_display_name();
|
||||
while let Some((indentation, thread_node_hash, has_sibling)) = iter.next() {
|
||||
let thread_node = &thread_nodes[&thread_node_hash];
|
||||
|
||||
|
@ -321,6 +332,8 @@ impl MailListingTrait for ThreadListing {
|
|||
prev_group = threads.find_group(thread_node.group);
|
||||
|
||||
let mut entry_strings = self.make_entry_string(&envelope, context);
|
||||
entry_strings.highlight_self =
|
||||
should_highlight_self && envelope.recipient_any(&my_address);
|
||||
entry_strings.subject = SubjectString(Self::make_thread_entry(
|
||||
&envelope,
|
||||
indentation,
|
||||
|
@ -363,11 +376,10 @@ impl MailListingTrait for ThreadListing {
|
|||
.try_into()
|
||||
.unwrap_or(255),
|
||||
);
|
||||
min_width.1 = cmp::max(min_width.1, entry_strings.date.grapheme_width()); /* date */
|
||||
min_width.2 = cmp::max(min_width.2, entry_strings.from.grapheme_width()); /* from */
|
||||
min_width.3 = cmp::max(min_width.3, entry_strings.flag.grapheme_width()); /* flags */
|
||||
min_width.4 = cmp::max(
|
||||
min_width.4,
|
||||
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
|
||||
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
|
||||
min_width.3 = min_width.3.max(entry_strings.flag.grapheme_width()); /* flags */
|
||||
min_width.4 = min_width.4.max(
|
||||
entry_strings.subject.grapheme_width()
|
||||
+ 1
|
||||
+ entry_strings.tags.grapheme_width(),
|
||||
|
@ -432,6 +444,12 @@ impl MailListingTrait for ThreadListing {
|
|||
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
|
||||
self.data_columns.segment_tree[4] = row_widths.4.into();
|
||||
|
||||
self.rows_drawn = SegmentTree::from(
|
||||
std::iter::repeat(1)
|
||||
.take(self.rows.len())
|
||||
.collect::<SmallVec<_>>(),
|
||||
);
|
||||
debug_assert_eq!(self.rows_drawn.array.len(), self.rows.len());
|
||||
self.draw_rows(
|
||||
context,
|
||||
0,
|
||||
|
@ -516,16 +534,14 @@ impl ListingTrait for ThreadListing {
|
|||
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
|
||||
|
||||
let top_idx = page_no * rows;
|
||||
let end_idx = self.length.saturating_sub(1).min(top_idx + rows - 1);
|
||||
self.draw_rows(context, top_idx, end_idx);
|
||||
|
||||
// If cursor position has changed, remove the highlight from the previous
|
||||
// position and apply it in the new one.
|
||||
if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no {
|
||||
let old_cursor_pos = self.cursor_pos;
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
for &(idx, highlight) in &[(old_cursor_pos.2, false), (self.new_cursor_pos.2, true)] {
|
||||
if idx >= self.length {
|
||||
continue; //bounds check
|
||||
|
@ -541,6 +557,10 @@ impl ListingTrait for ThreadListing {
|
|||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
self.draw_relative_numbers(grid, area, top_idx);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
if !self.force_draw {
|
||||
return;
|
||||
}
|
||||
|
@ -557,12 +577,6 @@ impl ListingTrait for ThreadListing {
|
|||
grid.clear_area(area, self.color_cache.theme_default);
|
||||
}
|
||||
|
||||
self.draw_rows(
|
||||
context,
|
||||
top_idx,
|
||||
self.length.saturating_sub(1).min(top_idx + rows - 1),
|
||||
);
|
||||
|
||||
// Page_no has changed, so draw new page
|
||||
_ = self.data_columns.recalc_widths(area.size(), top_idx);
|
||||
// copy table columns
|
||||
|
@ -745,6 +759,7 @@ impl ThreadListing {
|
|||
subsort: (Default::default(), Default::default()),
|
||||
color_cache: ColorCache::new(context, IndexStyle::Threaded),
|
||||
data_columns: DataColumns::default(),
|
||||
rows_drawn: SegmentTree::default(),
|
||||
rows: RowsState::default(),
|
||||
seen_cache: IndexMap::default(),
|
||||
filter_term: String::new(),
|
||||
|
@ -875,6 +890,7 @@ impl ThreadListing {
|
|||
),
|
||||
from: FromString(Address::display_name_slice(e.from())),
|
||||
tags: TagString(tags, colors),
|
||||
unseen: !e.is_seen(),
|
||||
highlight_self: false,
|
||||
}
|
||||
}
|
||||
|
@ -884,6 +900,12 @@ impl ThreadListing {
|
|||
return;
|
||||
}
|
||||
debug_assert!(end >= start);
|
||||
if self.rows_drawn.get_max(start, end) == 0 {
|
||||
return;
|
||||
}
|
||||
for i in start..=end {
|
||||
self.rows_drawn.update(i, 0);
|
||||
}
|
||||
let min_width = (
|
||||
self.data_columns.columns[0].area().width(),
|
||||
self.data_columns.columns[1].area().width(),
|
||||
|
@ -891,6 +913,7 @@ impl ThreadListing {
|
|||
self.data_columns.columns[3].area().width(),
|
||||
self.data_columns.columns[4].area().width(),
|
||||
);
|
||||
let columns = &mut self.data_columns.columns;
|
||||
|
||||
for (idx, ((_thread_hash, env_hash), strings)) in self
|
||||
.rows
|
||||
|
@ -907,152 +930,159 @@ impl ThreadListing {
|
|||
self.color_cache,
|
||||
even: idx % 2 == 0,
|
||||
unseen: !self.seen_cache[env_hash],
|
||||
highlighted: self.cursor_pos.2 == idx,
|
||||
selected: self.rows.selection[env_hash]
|
||||
highlighted: false,
|
||||
selected: false,
|
||||
);
|
||||
self.rows.row_attr_cache.insert(idx, row_attr);
|
||||
{
|
||||
let area = self.data_columns.columns[0].area();
|
||||
let mut area_col_0 = columns[0].area().nth_row(idx);
|
||||
if !*account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
|
||||
let (x, _) = self.data_columns.columns[0].grid_mut().write_string(
|
||||
area_col_0 = area_col_0.skip_cols(columns[0].grid_mut().write_string(
|
||||
&idx.to_string(),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(idx),
|
||||
area_col_0,
|
||||
None,
|
||||
);
|
||||
for x in x..min_width.0 {
|
||||
self.data_columns.columns[0].grid_mut()[(x, idx)]
|
||||
));
|
||||
for c in columns[0].grid().row_iter(area_col_0, 0..min_width.0, 0) {
|
||||
columns[0].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let area = self.data_columns.columns[1].area();
|
||||
let (x, _) = self.data_columns.columns[1].grid_mut().write_string(
|
||||
let mut area_col_1 = columns[1].area().nth_row(idx);
|
||||
area_col_1 = area_col_1.skip_cols(columns[1].grid_mut().write_string(
|
||||
&strings.date,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(idx),
|
||||
area_col_1,
|
||||
None,
|
||||
);
|
||||
for x in x..min_width.1 {
|
||||
self.data_columns.columns[1].grid_mut()[(x, idx)]
|
||||
));
|
||||
for c in columns[1].grid().row_iter(area_col_1, 0..min_width.1, 0) {
|
||||
columns[1].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
{
|
||||
let area = self.data_columns.columns[2].area();
|
||||
let (x, _) = self.data_columns.columns[2].grid_mut().write_string(
|
||||
let area_col_2 = columns[2].area().nth_row(idx);
|
||||
let (skip_cols, _) = columns[2].grid_mut().write_string(
|
||||
&strings.from,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(idx),
|
||||
area_col_2,
|
||||
None,
|
||||
);
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in crate::conf::text_format_regexps(context, "listing.from")
|
||||
{
|
||||
let t = self.data_columns.columns[2]
|
||||
.grid_mut()
|
||||
.insert_tag(text_formatter.tag);
|
||||
let t = columns[2].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in text_formatter.regexp.find_iter(strings.from.as_str()) {
|
||||
self.data_columns.columns[2].grid_mut().set_tag(
|
||||
columns[2].grid_mut().set_tag(
|
||||
t,
|
||||
(start, idx),
|
||||
(end, idx),
|
||||
(start + skip_cols, idx),
|
||||
(end + skip_cols, idx),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for x in x..min_width.2 {
|
||||
self.data_columns.columns[2].grid_mut()[(x, idx)]
|
||||
for c in columns[2]
|
||||
.grid()
|
||||
.row_iter(area_col_2, skip_cols..min_width.2, 0)
|
||||
{
|
||||
columns[2].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
{
|
||||
let area = self.data_columns.columns[3].area();
|
||||
let (x, _) = self.data_columns.columns[3].grid_mut().write_string(
|
||||
let mut area_col_3 = columns[3].area().nth_row(idx);
|
||||
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
|
||||
&strings.flag,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(idx),
|
||||
area_col_3,
|
||||
None,
|
||||
);
|
||||
for x in x..min_width.3 {
|
||||
self.data_columns.columns[3].grid_mut()[(x, idx)]
|
||||
));
|
||||
if strings.highlight_self {
|
||||
let (x, _) = columns[3].grid_mut().write_string(
|
||||
mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
|
||||
self.color_cache.highlight_self.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs | Attr::FORCE_TEXT,
|
||||
area_col_3,
|
||||
None,
|
||||
);
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
|
||||
columns[3].grid_mut()[c].set_keep_fg(true);
|
||||
}
|
||||
area_col_3 = area_col_3.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
|
||||
columns[3].grid_mut()[c]
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
}
|
||||
{
|
||||
let area = self.data_columns.columns[4].area();
|
||||
let (x, _) = self.data_columns.columns[4].grid_mut().write_string(
|
||||
let mut area_col_4 = columns[4].area().nth_row(idx);
|
||||
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
|
||||
&strings.subject,
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(idx),
|
||||
area_col_4,
|
||||
None,
|
||||
);
|
||||
));
|
||||
#[cfg(feature = "regexp")]
|
||||
{
|
||||
for text_formatter in
|
||||
crate::conf::text_format_regexps(context, "listing.subject")
|
||||
{
|
||||
let t = self.data_columns.columns[4]
|
||||
.grid_mut()
|
||||
.insert_tag(text_formatter.tag);
|
||||
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
|
||||
for (start, end) in
|
||||
text_formatter.regexp.find_iter(strings.subject.as_str())
|
||||
{
|
||||
self.data_columns.columns[4].grid_mut().set_tag(
|
||||
t,
|
||||
(start, idx),
|
||||
(end, idx),
|
||||
);
|
||||
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
let x = {
|
||||
let mut x = x + 1;
|
||||
let area = self.data_columns.columns[4].area();
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let (_x, _) = self.data_columns.columns[4].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area.nth_row(idx).skip_cols(x + 1),
|
||||
None,
|
||||
);
|
||||
self.data_columns.columns[4].grid_mut()[(x, idx)].set_bg(color);
|
||||
if _x < min_width.4 {
|
||||
self.data_columns.columns[4].grid_mut()[(_x, idx)]
|
||||
.set_bg(color)
|
||||
.set_keep_bg(true);
|
||||
}
|
||||
for x in (x + 1).._x {
|
||||
self.data_columns.columns[4].grid_mut()[(x, idx)]
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
self.data_columns.columns[4].grid_mut()[(x, idx)].set_keep_bg(true);
|
||||
x = _x + 1;
|
||||
area_col_4 = area_col_4.skip_cols(1);
|
||||
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
|
||||
let color = color.unwrap_or(self.color_cache.tag_default.bg);
|
||||
let (x, _) = columns[4].grid_mut().write_string(
|
||||
t,
|
||||
self.color_cache.tag_default.fg,
|
||||
color,
|
||||
self.color_cache.tag_default.attrs,
|
||||
area_col_4.skip_cols(1),
|
||||
None,
|
||||
);
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_bg(color)
|
||||
.set_keep_fg(true)
|
||||
.set_keep_bg(true)
|
||||
.set_keep_attrs(true);
|
||||
}
|
||||
x
|
||||
};
|
||||
for x in x..min_width.4 {
|
||||
self.data_columns.columns[4].grid_mut()[(x, idx)]
|
||||
area_col_4 = area_col_4.skip_cols(x + 1);
|
||||
}
|
||||
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
|
||||
columns[4].grid_mut()[c]
|
||||
.set_ch(' ')
|
||||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
|
@ -1081,24 +1111,52 @@ impl ThreadListing {
|
|||
);
|
||||
self.seen_cache.insert(env_hash, envelope.is_seen());
|
||||
|
||||
let mut strings = self.make_entry_string(&envelope, context);
|
||||
let should_highlight_self = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.highlight_self
|
||||
)
|
||||
.is_true();
|
||||
let mut entry_strings = self.make_entry_string(&envelope, context);
|
||||
entry_strings.highlight_self = should_highlight_self && {
|
||||
let my_address: Address = context.accounts[&self.cursor_pos.0]
|
||||
.settings
|
||||
.account
|
||||
.make_display_name();
|
||||
envelope.recipient_any(&my_address)
|
||||
};
|
||||
// [ref:FIXME]: generate new tree indentation for this new row subject
|
||||
// entry_strings.subject = SubjectString(Self::make_thread_entry(
|
||||
// &envelope,
|
||||
// indentation,
|
||||
// thread_node_hash,
|
||||
// &threads,
|
||||
// &indentations,
|
||||
// has_sibling,
|
||||
// is_root,
|
||||
// ));
|
||||
drop(envelope);
|
||||
std::mem::swap(
|
||||
&mut self.rows.entries.get_mut(idx).unwrap().1.subject,
|
||||
&mut strings.subject,
|
||||
&mut entry_strings.subject,
|
||||
);
|
||||
let columns = &mut self.data_columns.columns;
|
||||
for n in 0..=4 {
|
||||
let area = columns[n].area().nth_row(idx);
|
||||
columns[n].grid_mut().clear_area(area, row_attr);
|
||||
}
|
||||
self.rows_drawn.update(idx, 1);
|
||||
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), entry_strings);
|
||||
}
|
||||
|
||||
fn draw_relative_numbers(&mut self, grid: &mut CellBuffer, area: Area, top_idx: usize) {
|
||||
let width = self.data_columns.columns[0].area().width();
|
||||
let area = area.take_cols(width);
|
||||
for i in 0..area.height() {
|
||||
if top_idx + i >= self.length {
|
||||
break;
|
||||
}
|
||||
let row_attr = if let Some(env_hash) = self.get_env_under_cursor(top_idx + i) {
|
||||
row_attr!(
|
||||
self.color_cache,
|
||||
|
@ -1111,27 +1169,7 @@ impl ThreadListing {
|
|||
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
|
||||
};
|
||||
|
||||
let idx_col_area = self.data_columns.columns[0].area();
|
||||
self.data_columns.columns[0]
|
||||
.grid_mut()
|
||||
.clear_area(idx_col_area, row_attr);
|
||||
|
||||
grid.clear_area(area.nth_row(i).take_cols(width), row_attr);
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
|
||||
self.new_cursor_pos.2.to_string()
|
||||
} else {
|
||||
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
|
||||
.abs()
|
||||
.to_string()
|
||||
},
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
idx_col_area.nth_row(i),
|
||||
None,
|
||||
);
|
||||
|
||||
grid.clear_area(area.nth_row(i), row_attr);
|
||||
grid.write_string(
|
||||
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
|
||||
self.new_cursor_pos.2.to_string()
|
||||
|
@ -1143,7 +1181,7 @@ impl ThreadListing {
|
|||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
row_attr.attrs,
|
||||
area.nth_row(i).take_cols(width),
|
||||
area.nth_row(i),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
@ -1425,7 +1463,6 @@ impl Component for ThreadListing {
|
|||
if self.force_draw {
|
||||
/* Draw the entire list */
|
||||
self.draw_list(grid, area, context);
|
||||
self.force_draw = false;
|
||||
}
|
||||
} else {
|
||||
/* Draw the entire list */
|
||||
|
@ -1438,6 +1475,7 @@ impl Component for ThreadListing {
|
|||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
}
|
||||
self.force_draw = false;
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
|
@ -1602,9 +1640,9 @@ impl Component for ThreadListing {
|
|||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some("Could not perform search".into()),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
||||
source: Some(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1628,9 +1666,9 @@ impl Component for ThreadListing {
|
|||
Ok(Some(Err(err))) => {
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some("Could not perform search".into()),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
||||
source: Some(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ impl ScreenGeneration {
|
|||
pub const NIL: Self = Self((0, 0));
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn next(self) -> Self {
|
||||
Self(uuid::Uuid::new_v4().as_u64_pair())
|
||||
}
|
||||
|
@ -521,6 +522,30 @@ impl<D: private::Sealed> From<&Screen<D>> for Area {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience trait to turn both single `usize` values and `(usize, _)`
|
||||
/// positions to `x` coordinate.
|
||||
pub trait IntoColumns: private::Sealed {
|
||||
#[must_use]
|
||||
fn into(self) -> usize;
|
||||
}
|
||||
|
||||
impl private::Sealed for usize {}
|
||||
impl private::Sealed for Pos {}
|
||||
|
||||
impl IntoColumns for usize {
|
||||
#[must_use]
|
||||
fn into(self) -> usize {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoColumns for Pos {
|
||||
#[must_use]
|
||||
fn into(self) -> usize {
|
||||
get_x(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Area {
|
||||
#[inline]
|
||||
pub fn height(&self) -> usize {
|
||||
|
@ -545,6 +570,7 @@ impl Area {
|
|||
|
||||
/// Get `n`th row of `area` or its last one.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nth_row(&self, n: usize) -> Self {
|
||||
let Self {
|
||||
offset,
|
||||
|
@ -574,6 +600,7 @@ impl Area {
|
|||
|
||||
/// Get `n`th col of `area` or its last one.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nth_col(&self, n: usize) -> Self {
|
||||
let Self {
|
||||
offset,
|
||||
|
@ -602,6 +629,7 @@ impl Area {
|
|||
}
|
||||
|
||||
/// Place box given by `(width, height)` in corner of `area`
|
||||
#[must_use]
|
||||
pub fn place_inside(&self, (width, height): (usize, usize), upper: bool, left: bool) -> Self {
|
||||
if self.is_empty() || width < 3 || height < 3 {
|
||||
return *self;
|
||||
|
@ -644,6 +672,7 @@ impl Area {
|
|||
|
||||
/// Place given area of dimensions `(width, height)` inside `area` according
|
||||
/// to given alignment
|
||||
#[must_use]
|
||||
pub fn align_inside(
|
||||
&self,
|
||||
(width, height): (usize, usize),
|
||||
|
@ -677,6 +706,7 @@ impl Area {
|
|||
|
||||
/// Place box given by `dimensions` in center of `area`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn center_inside(&self, dimensions: (usize, usize)) -> Self {
|
||||
self.align_inside(dimensions, Alignment::Center, Alignment::Center)
|
||||
}
|
||||
|
@ -712,6 +742,7 @@ impl Area {
|
|||
/// assert_eq!(body.height(), 18);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn skip_rows(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.height());
|
||||
if self.is_empty() || self.upper_left.1 + n > self.bottom_right.1 {
|
||||
|
@ -743,6 +774,7 @@ impl Area {
|
|||
/// assert_eq!(header, area.take_rows(2));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn skip_rows_from_end(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.height());
|
||||
if self.is_empty() || self.bottom_right.1 < n {
|
||||
|
@ -755,6 +787,21 @@ impl Area {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn _skip_cols_inner(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.width());
|
||||
if self.is_empty() || self.bottom_right.0 < self.upper_left.0 + n {
|
||||
return self.into_empty();
|
||||
}
|
||||
|
||||
Self {
|
||||
offset: pos_inc(self.offset, (n, 0)),
|
||||
upper_left: pos_inc(self.upper_left, (n, 0)),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip the first `n` rows and return the remaining area.
|
||||
/// Return value will be an empty area if `n` is more than the width.
|
||||
///
|
||||
|
@ -772,17 +819,10 @@ impl Area {
|
|||
/// assert_eq!(indent.width(), 118);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn skip_cols(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.width());
|
||||
if self.is_empty() || self.bottom_right.0 < self.upper_left.0 + n {
|
||||
return self.into_empty();
|
||||
}
|
||||
|
||||
Self {
|
||||
offset: pos_inc(self.offset, (n, 0)),
|
||||
upper_left: pos_inc(self.upper_left, (n, 0)),
|
||||
..*self
|
||||
}
|
||||
#[must_use]
|
||||
pub fn skip_cols(&self, n: impl IntoColumns) -> Self {
|
||||
let n: usize = n.into();
|
||||
self._skip_cols_inner(n)
|
||||
}
|
||||
|
||||
/// Skip the last `n` rows and return the remaining area.
|
||||
|
@ -803,6 +843,7 @@ impl Area {
|
|||
/// assert_eq!(indent, area.take_cols(118));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn skip_cols_from_end(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.width());
|
||||
if self.is_empty() || self.bottom_right.0 < n {
|
||||
|
@ -816,6 +857,7 @@ impl Area {
|
|||
|
||||
/// Shortcut for using `Area::skip_cols` and `Area::skip_rows` together.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn skip(&self, n_cols: usize, n_rows: usize) -> Self {
|
||||
self.skip_cols(n_cols).skip_rows(n_rows)
|
||||
}
|
||||
|
@ -837,6 +879,7 @@ impl Area {
|
|||
/// assert_eq!(header.height(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn take_rows(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.height());
|
||||
if self.is_empty() || self.bottom_right.1 < (self.height() - n) {
|
||||
|
@ -869,6 +912,7 @@ impl Area {
|
|||
/// assert_eq!(header.width(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn take_cols(&self, n: usize) -> Self {
|
||||
let n = std::cmp::min(n, self.width());
|
||||
if self.is_empty() || self.bottom_right.0 < (self.width() - n) {
|
||||
|
@ -886,41 +930,49 @@ impl Area {
|
|||
|
||||
/// Shortcut for using `Area::take_cols` and `Area::take_rows` together.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn take(&self, n_cols: usize, n_rows: usize) -> Self {
|
||||
self.take_cols(n_cols).take_rows(n_rows)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn upper_left(&self) -> Pos {
|
||||
self.upper_left
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn bottom_right(&self) -> Pos {
|
||||
self.bottom_right
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn upper_right(&self) -> Pos {
|
||||
set_x(self.upper_left, get_x(self.bottom_right))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn bottom_left(&self) -> Pos {
|
||||
set_y(self.upper_left, get_y(self.bottom_right))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn offset(&self) -> Pos {
|
||||
self.offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn generation(&self) -> ScreenGeneration {
|
||||
self.generation
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new_empty(generation: ScreenGeneration) -> Self {
|
||||
Self {
|
||||
offset: (0, 0),
|
||||
|
@ -934,6 +986,7 @@ impl Area {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn into_empty(self) -> Self {
|
||||
Self {
|
||||
offset: (0, 0),
|
||||
|
@ -945,6 +998,7 @@ impl Area {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.empty
|
||||
|| (self.upper_left.0 > self.bottom_right.0 || self.upper_left.1 > self.bottom_right.1)
|
||||
|
@ -952,26 +1006,31 @@ impl Area {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
const fn pos_inc(p: Pos, inc: (usize, usize)) -> Pos {
|
||||
(p.0 + inc.0, p.1 + inc.1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
const fn get_x(p: Pos) -> usize {
|
||||
p.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
const fn get_y(p: Pos) -> usize {
|
||||
p.1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
const fn set_x(p: Pos, new_x: usize) -> Pos {
|
||||
(new_x, p.1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
const fn set_y(p: Pos, new_y: usize) -> Pos {
|
||||
(p.0, new_y)
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ impl<const N: usize> DataColumns<N> {
|
|||
width_accum += self.widths[i];
|
||||
}
|
||||
// add column gaps
|
||||
width_accum += 2 * N.saturating_sub(1);
|
||||
width_accum += N.saturating_sub(1);
|
||||
debug_assert!(growees >= growees_max);
|
||||
if width_accum >= screen_width || screen_height == 0 || screen_width == 0 || growees == 0 {
|
||||
self.width_accum = width_accum;
|
||||
|
@ -260,7 +260,7 @@ impl<const N: usize> DataColumns<N> {
|
|||
break;
|
||||
}
|
||||
x_offset -= self.widths[col];
|
||||
x_offset = x_offset.saturating_sub(2);
|
||||
x_offset = x_offset.saturating_sub(1);
|
||||
}
|
||||
|
||||
for col in start_col..N {
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.8.5"
|
|||
authors = ["Manos Pitsidianakis <manos@pitsidianak.is>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
rust-version = "1.68.2"
|
||||
rust-version = "1.70.0"
|
||||
|
||||
homepage = "https://meli-email.org"
|
||||
repository = "https://git.meli-email.org/meli/meli.git"
|
||||
|
@ -83,5 +83,5 @@ vcard = []
|
|||
flate2 = { version = "1.0.16" }
|
||||
|
||||
[dev-dependencies]
|
||||
mailin-embedded = { version = "0.7", features = ["rtls"] }
|
||||
mailin-embedded = { version = "0.8", features = ["rtls"] }
|
||||
stderrlog = "^0.5"
|
||||
|
|
|
@ -859,6 +859,16 @@ impl Envelope {
|
|||
pub fn tags_mut(&mut self) -> &mut IndexSet<TagHash> {
|
||||
&mut self.tags
|
||||
}
|
||||
|
||||
/// Returns `true` if `is_recipient` address is included in To:, Cc: or Bcc:
|
||||
/// headers.
|
||||
pub fn recipient_any(&self, is_recipient: &Address) -> bool {
|
||||
self.to()
|
||||
.iter()
|
||||
.chain(self.cc().iter())
|
||||
.chain(self.bcc().iter())
|
||||
.any(|a| a == is_recipient)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Envelope {}
|
||||
|
|
|
@ -335,6 +335,7 @@ pub enum ErrorKind {
|
|||
Network(NetworkErrorKind),
|
||||
TimedOut,
|
||||
OSError,
|
||||
Platform,
|
||||
NotImplemented,
|
||||
NotSupported,
|
||||
ValueError,
|
||||
|
@ -354,6 +355,7 @@ impl std::fmt::Display for ErrorKind {
|
|||
fmt,
|
||||
"Protocol is not supported. It could be the wrong type or version."
|
||||
),
|
||||
Self::Platform => write!(fmt, "Platform/Runtime environment; OS or hardware"),
|
||||
Self::TimedOut => write!(fmt, "Timed Out"),
|
||||
Self::OSError => write!(fmt, "OS Error"),
|
||||
Self::Configuration => write!(fmt, "Configuration"),
|
||||
|
|
|
@ -1334,12 +1334,24 @@ pub fn quoted(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
|||
}
|
||||
|
||||
let mut i = 1;
|
||||
let mut escape_next = false;
|
||||
while i < input.len() {
|
||||
if input[i] == b'\"' && input[i - 1] != b'\\' {
|
||||
return match crate::email::parser::encodings::phrase(&input[1..i], false) {
|
||||
Ok((_, out)) => Ok((&input[i + 1..], out)),
|
||||
e => e,
|
||||
};
|
||||
match (input[i], escape_next) {
|
||||
(b'\\', false) => {
|
||||
escape_next = true;
|
||||
}
|
||||
(b'\\', true) => {
|
||||
escape_next = false;
|
||||
}
|
||||
(b'\"', false) => {
|
||||
return match crate::email::parser::encodings::phrase(&input[1..i], false) {
|
||||
Ok((_, out)) => Ok((&input[i + 1..], out)),
|
||||
e => e,
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
escape_next = false;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
|
|
@ -169,6 +169,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!("●".grapheme_width(), 1);
|
||||
assert_eq!("●📎".grapheme_width(), 3);
|
||||
assert_eq!("●📎︎".grapheme_width(), 3);
|
||||
assert_eq!("●\u{FE0E}📎\u{FE0E}".grapheme_width(), 3);
|
||||
assert_eq!("🎃".grapheme_width(), 2);
|
||||
assert_eq!("👻".grapheme_width(), 2);
|
||||
|
|
|
@ -132,6 +132,66 @@ macro_rules! syscall {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Hardcoded `setsockopt` arguments for type safety when calling
|
||||
/// [`Connection::setsockopt`] in an `unsafe` block.
|
||||
///
|
||||
/// Add new variants when you need to call `setsockopt` with new arguments.
|
||||
pub enum SockOpts {
|
||||
/// Set TCP Keep Alive.
|
||||
///
|
||||
/// Following text is sourced from <https://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/>.
|
||||
///
|
||||
/// ```text
|
||||
/// 4.2. The setsockopt function call
|
||||
///
|
||||
/// All you need to enable keepalive for a specific socket is to set the specific socket option
|
||||
/// on the socket itself. The prototype of the function is as follows:
|
||||
///
|
||||
///
|
||||
/// int setsockopt(int s, int level, int optname,
|
||||
/// const void *optval, socklen_t optlen)
|
||||
///
|
||||
///
|
||||
/// The first parameter is the socket, previously created with the socket(2); the second one
|
||||
/// must be SOL_SOCKET, and the third must be SO_KEEPALIVE . The fourth parameter must be a
|
||||
/// boolean integer value, indicating that we want to enable the option, while the last is the
|
||||
/// size of the value passed before.
|
||||
///
|
||||
/// According to the manpage, 0 is returned upon success, and -1 is returned on error (and
|
||||
/// errno is properly set).
|
||||
///
|
||||
/// There are also three other socket options you can set for keepalive when you write your
|
||||
/// application. They all use the SOL_TCP level instead of SOL_SOCKET, and they override
|
||||
/// system-wide variables only for the current socket. If you read without writing first, the
|
||||
/// current system-wide parameters will be returned.
|
||||
///
|
||||
/// TCP_KEEPCNT: overrides tcp_keepalive_probes
|
||||
///
|
||||
/// TCP_KEEPIDLE: overrides tcp_keepalive_time
|
||||
///
|
||||
/// TCP_KEEPINTVL: overrides tcp_keepalive_intvl
|
||||
/// ```
|
||||
///
|
||||
/// Field `duration` overrides `tcp_keepalive_time`:
|
||||
///
|
||||
/// ```text
|
||||
/// tcp_keepalive_time
|
||||
///
|
||||
/// the interval between the last data packet sent (simple ACKs are not considered data) and the
|
||||
/// first keepalive probe; after the connection is marked to need keepalive, this counter is not
|
||||
/// used any further
|
||||
/// ```
|
||||
///
|
||||
/// The default value in the Linux kernel is 7200 seconds (2 hours).
|
||||
KeepAlive {
|
||||
enable: bool,
|
||||
duration: Option<Duration>,
|
||||
},
|
||||
TcpNoDelay {
|
||||
enable: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub const IO_BUF_SIZE: usize = 64 * 1024;
|
||||
|
||||
|
@ -160,11 +220,14 @@ impl Connection {
|
|||
}
|
||||
|
||||
pub fn new_tcp(inner: std::net::TcpStream) -> Self {
|
||||
Self::Tcp {
|
||||
let ret = Self::Tcp {
|
||||
inner,
|
||||
id: None,
|
||||
trace: false,
|
||||
}
|
||||
};
|
||||
_ = ret.setsockopt(SockOpts::TcpNoDelay { enable: true });
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn trace(mut self, val: bool) -> Self {
|
||||
|
@ -288,11 +351,11 @@ impl Connection {
|
|||
return Ok(None);
|
||||
}
|
||||
unsafe {
|
||||
let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?;
|
||||
let raw: c_int = self.__getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?;
|
||||
if raw == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
let secs: c_int = self.getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?;
|
||||
let secs: c_int = self.__getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?;
|
||||
Ok(Some(Duration::new(secs as u64, 0)))
|
||||
}
|
||||
}
|
||||
|
@ -312,21 +375,13 @@ impl Connection {
|
|||
if matches!(self, Fd { .. }) {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
self.setsockopt(
|
||||
libc::SOL_SOCKET,
|
||||
libc::SO_KEEPALIVE,
|
||||
keepalive.is_some() as c_int,
|
||||
)?;
|
||||
if let Some(dur) = keepalive {
|
||||
// [ref:TODO]: checked cast here
|
||||
self.setsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION, dur.as_secs() as c_int)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
self.setsockopt(SockOpts::KeepAlive {
|
||||
enable: keepalive.is_some(),
|
||||
duration: keepalive,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> std::io::Result<()>
|
||||
unsafe fn inner_setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> std::io::Result<()>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
@ -341,7 +396,44 @@ impl Connection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> std::io::Result<T> {
|
||||
fn setsockopt(&self, option: SockOpts) -> std::io::Result<()> {
|
||||
match option {
|
||||
SockOpts::KeepAlive {
|
||||
enable: true,
|
||||
duration,
|
||||
} => {
|
||||
unsafe {
|
||||
self.inner_setsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE, <c_int>::from(true))
|
||||
}?;
|
||||
if let Some(dur) = duration {
|
||||
unsafe {
|
||||
self.inner_setsockopt(
|
||||
libc::IPPROTO_TCP,
|
||||
KEEPALIVE_OPTION,
|
||||
dur.as_secs() as c_int,
|
||||
)
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
SockOpts::KeepAlive {
|
||||
enable: false,
|
||||
duration: _,
|
||||
} => unsafe {
|
||||
self.inner_setsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE, <c_int>::from(false))
|
||||
},
|
||||
SockOpts::TcpNoDelay { enable } => unsafe {
|
||||
self.inner_setsockopt(
|
||||
libc::SOL_TCP,
|
||||
libc::TCP_NODELAY,
|
||||
if enable { c_int::from(1_u8) } else { 0 },
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> std::io::Result<T> {
|
||||
let mut slot: T = std::mem::zeroed();
|
||||
let mut len = std::mem::size_of::<T>() as libc::socklen_t;
|
||||
syscall!(getsockopt(
|
||||
|
|
Loading…
Reference in New Issue