fix(entry): always preserve raw input + match ranges conversions (#62)

* fix(entry): preserve raw input

* chore(version): bump workspace crates and television

* test: add tests for replace_non_printable and cleanup commented out code

* chore(changelog): update changelog (auto)

* chore(deps): update cargo dependencies

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Alexandre Pasmantier 2024-11-24 00:20:04 +01:00 committed by GitHub
parent b703e1b26c
commit edd9df4e29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 419 additions and 274 deletions

View File

@ -36,5 +36,5 @@ jobs:
git config user.email 'github-actions[bot]@users.noreply.github.com' git config user.email 'github-actions[bot]@users.noreply.github.com'
set +e set +e
git add CHANGELOG.md git add CHANGELOG.md
git commit -m "Update changelog" git commit -m "chore(changelog): update changelog (auto)"
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git ${{ steps.extract_branch.outputs.branch }} git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git ${{ steps.extract_branch.outputs.branch }}

View File

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### 🐛 Bug Fixes ### 🐛 Bug Fixes
- Quote file names that contain spaces when printing them to stdout (#51) - Quote file names that contain spaces when printing them to stdout (#51)
- *(entry)* Preserve raw input
### 🚜 Refactor ### 🚜 Refactor
@ -15,15 +16,20 @@ All notable changes to this project will be documented in this file.
### 📚 Documentation ### 📚 Documentation
- Terminal emulators compatibility and good first issues (#56) - Terminal emulators compatibility and good first issues (#56)
- *(contributing)* Add setup step
### 🎨 Styling ### 🎨 Styling
- *(git)* Enforce conventional commits on git push with a hook - *(git)* Enforce conventional commits on git push with a hook (#61)
### 🧪 Testing
- Add tests for replace_non_printable and cleanup commented out code
### ⚙️ Miscellaneous Tasks ### ⚙️ Miscellaneous Tasks
- Add readme version update to github actions (#55) - Add readme version update to github actions (#55)
- *(version)* Bump workspace crates and television
- *(changelog)* Update changelog (auto)
### Build ### Build

150
Cargo.lock generated
View File

@ -249,20 +249,20 @@ dependencies = [
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.10.0" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.9",
"serde", "serde",
] ]
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.19.0" version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
[[package]] [[package]]
name = "bytes" name = "bytes"
@ -351,9 +351,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.37" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -366,9 +366,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.20" version = "4.5.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -376,9 +376,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.20" version = "4.5.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -400,9 +400,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
@ -575,9 +575,9 @@ dependencies = [
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.14" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -749,6 +749,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -905,9 +911,9 @@ dependencies = [
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.34" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide 0.8.0", "miniz_oxide 0.8.0",
@ -1484,7 +1490,7 @@ dependencies = [
"aho-corasick", "aho-corasick",
"bstr", "bstr",
"log", "log",
"regex-automata 0.4.8", "regex-automata 0.4.9",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
] ]
@ -1716,7 +1722,7 @@ dependencies = [
"globset", "globset",
"log", "log",
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.9",
"same-file", "same-file",
"walkdir", "walkdir",
"winapi-util", "winapi-util",
@ -1746,10 +1752,14 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]] [[package]]
name = "instability" name = "instability"
version = "0.3.2" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e"
dependencies = [ dependencies = [
"darling",
"indoc",
"pretty_assertions",
"proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
@ -1771,9 +1781,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2"
[[package]] [[package]]
name = "jiff" name = "jiff"
@ -1825,9 +1835,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.162" version = "0.2.164"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -1864,9 +1874,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -2275,10 +2285,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro2" name = "pretty_assertions"
version = "1.0.89" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -2386,7 +2406,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.9",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
] ]
@ -2401,9 +2421,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.8" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -2470,9 +2490,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.40" version = "0.38.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"errno", "errno",
@ -2525,18 +2545,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.214" version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.214" version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2545,9 +2565,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.132" version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -2748,9 +2768,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.87" version = "2.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2792,7 +2812,7 @@ dependencies = [
[[package]] [[package]]
name = "television" name = "television"
version = "0.5.1" version = "0.5.2"
dependencies = [ dependencies = [
"better-panic", "better-panic",
"clap", "clap",
@ -2823,7 +2843,7 @@ dependencies = [
[[package]] [[package]]
name = "television-channels" name = "television-channels"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
@ -2842,7 +2862,7 @@ dependencies = [
[[package]] [[package]]
name = "television-derive" name = "television-derive"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2851,7 +2871,7 @@ dependencies = [
[[package]] [[package]]
name = "television-fuzzy" name = "television-fuzzy"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"nucleo", "nucleo",
"parking_lot", "parking_lot",
@ -2859,7 +2879,7 @@ dependencies = [
[[package]] [[package]]
name = "television-previewers" name = "television-previewers"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"devicons", "devicons",
@ -2874,7 +2894,7 @@ dependencies = [
[[package]] [[package]]
name = "television-utils" name = "television-utils"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"bat", "bat",
"color-eyre", "color-eyre",
@ -3158,9 +3178,9 @@ checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@ -3208,9 +3228,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.3" version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
@ -3670,10 +3690,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "yoke" name = "yansi"
version = "0.7.4" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yoke"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
dependencies = [ dependencies = [
"serde", "serde",
"stable_deref_trait", "stable_deref_trait",
@ -3683,9 +3709,9 @@ dependencies = [
[[package]] [[package]]
name = "yoke-derive" name = "yoke-derive"
version = "0.7.4" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3715,18 +3741,18 @@ dependencies = [
[[package]] [[package]]
name = "zerofrom" name = "zerofrom"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
dependencies = [ dependencies = [
"zerofrom-derive", "zerofrom-derive",
] ]
[[package]] [[package]]
name = "zerofrom-derive" name = "zerofrom-derive"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television" name = "television"
version = "0.5.1" version = "0.5.2"
edition = "2021" edition = "2021"
description = "The revolution will be televised." description = "The revolution will be televised."
license = "MIT" license = "MIT"
@ -54,11 +54,11 @@ name = "tv"
[dependencies] [dependencies]
# workspace dependencies # workspace dependencies
television-fuzzy = { path = "crates/television-fuzzy", version = "0.0.5" } television-fuzzy = { path = "crates/television-fuzzy", version = "0.0.6" }
television-derive = { path = "crates/television-derive", version = "0.0.5" } television-derive = { path = "crates/television-derive", version = "0.0.6" }
television-channels = { path = "crates/television-channels", version = "0.0.5" } television-channels = { path = "crates/television-channels", version = "0.0.6" }
television-previewers = { path = "crates/television-previewers", version = "0.0.5" } television-previewers = { path = "crates/television-previewers", version = "0.0.6" }
television-utils = { path = "crates/television-utils", version = "0.0.5" } television-utils = { path = "crates/television-utils", version = "0.0.6" }
# external dependencies # external dependencies
better-panic = "0.3.0" better-panic = "0.3.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television-channels" name = "television-channels"
version = "0.0.5" version = "0.0.6"
description.workspace = true description.workspace = true
authors.workspace = true authors.workspace = true
repository.workspace = true repository.workspace = true
@ -13,9 +13,9 @@ edition.workspace = true
rust-version.workspace = true rust-version.workspace = true
[dependencies] [dependencies]
television-fuzzy = { path = "../television-fuzzy", version = "0.0.5" } television-fuzzy = { path = "../television-fuzzy", version = "0.0.6" }
television-utils = { path = "../television-utils", version = "0.0.5" } television-utils = { path = "../television-utils", version = "0.0.6" }
television-derive = { path = "../television-derive", version = "0.0.5" } television-derive = { path = "../television-derive", version = "0.0.6" }
devicons = "0.6.11" devicons = "0.6.11"
tracing = "0.1.40" tracing = "0.1.40"
eyre = "0.6.12" eyre = "0.6.12"

View File

@ -4,7 +4,6 @@ use crate::entry::PreviewType;
use devicons::FileIcon; use devicons::FileIcon;
use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher}; use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher};
use television_utils::indices::sep_name_and_value_indices; use television_utils::indices::sep_name_and_value_indices;
use television_utils::strings::preprocess_line;
use tracing::debug; use tracing::debug;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -146,8 +145,8 @@ async fn load_aliases(injector: Injector<Alias>) {
if let Some(name) = parts.next() { if let Some(name) = parts.next() {
if let Some(value) = parts.next() { if let Some(value) = parts.next() {
return Some(Alias::new( return Some(Alias::new(
preprocess_line(name), name.to_string(),
preprocess_line(value), value.to_string(),
)); ));
} }
} }

View File

@ -4,7 +4,6 @@ use super::OnAir;
use crate::entry::{Entry, PreviewType}; use crate::entry::{Entry, PreviewType};
use television_fuzzy::matcher::{config::Config, Matcher}; use television_fuzzy::matcher::{config::Config, Matcher};
use television_utils::indices::sep_name_and_value_indices; use television_utils::indices::sep_name_and_value_indices;
use television_utils::strings::preprocess_line;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EnvVar { struct EnvVar {
@ -26,15 +25,9 @@ impl Channel {
let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS)); let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS));
let injector = matcher.injector(); let injector = matcher.injector();
for (name, value) in std::env::vars() { for (name, value) in std::env::vars() {
let () = injector.push( let () = injector.push(EnvVar { name, value }, |e, cols| {
EnvVar { cols[0] = (e.name.clone() + &e.value).into();
name: preprocess_line(&name), });
value: preprocess_line(&value),
},
|e, cols| {
cols[0] = (e.name.clone() + &e.value).into();
},
);
} }
Channel { Channel {
matcher, matcher,

View File

@ -5,7 +5,6 @@ use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher}; use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher};
use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS}; use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS};
use television_utils::strings::preprocess_line;
pub struct Channel { pub struct Channel {
matcher: Matcher<String>, matcher: Matcher<String>,
@ -58,7 +57,7 @@ impl From<&mut TelevisionChannel> for Channel {
Self::new( Self::new(
entries entries
.iter() .iter()
.map(|entry| PathBuf::from(entry.display_name())) .map(|entry| PathBuf::from(&entry.name))
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
.into_iter() .into_iter()
.collect(), .collect(),
@ -132,16 +131,15 @@ async fn load_files(paths: Vec<PathBuf>, injector: Injector<String>) {
Box::new(move |result| { Box::new(move |result| {
if let Ok(entry) = result { if let Ok(entry) = result {
if entry.file_type().unwrap().is_file() { if entry.file_type().unwrap().is_file() {
let file_path = preprocess_line( let file_path = &entry
&entry .path()
.path() .strip_prefix(&current_dir)
.strip_prefix(&current_dir) .unwrap_or(entry.path())
.unwrap_or(entry.path()) .to_string_lossy();
.to_string_lossy(), let () =
); injector.push(file_path.to_string(), |e, cols| {
let () = injector.push(file_path, |e, cols| { cols[0] = e.clone().into();
cols[0] = e.clone().into(); });
});
} }
} }
ignore::WalkState::Continue ignore::WalkState::Continue

View File

@ -9,7 +9,6 @@ use crate::channels::OnAir;
use crate::entry::{Entry, PreviewType}; use crate::entry::{Entry, PreviewType};
use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher}; use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher};
use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS}; use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS};
use television_utils::strings::preprocess_line;
pub struct Channel { pub struct Channel {
matcher: Matcher<String>, matcher: Matcher<String>,
@ -150,13 +149,15 @@ async fn crawl_for_repos(starting_point: PathBuf, injector: Injector<String>) {
if entry.file_type().unwrap().is_dir() { if entry.file_type().unwrap().is_dir() {
// if the entry is a .git directory, add its parent to the list of git repos // if the entry is a .git directory, add its parent to the list of git repos
if entry.path().ends_with(".git") { if entry.path().ends_with(".git") {
let parent_path = preprocess_line( let parent_path =
&entry.path().parent().unwrap().to_string_lossy(), &entry.path().parent().unwrap().to_string_lossy();
);
debug!("Found git repo: {:?}", parent_path); debug!("Found git repo: {:?}", parent_path);
let () = injector.push(parent_path, |e, cols| { let () = injector.push(
cols[0] = e.clone().into(); parent_path.to_string(),
}); |e, cols| {
cols[0] = e.clone().into();
},
);
return ignore::WalkState::Skip; return ignore::WalkState::Skip;
} }
} }

View File

@ -6,7 +6,6 @@ use devicons::FileIcon;
use super::OnAir; use super::OnAir;
use crate::entry::{Entry, PreviewType}; use crate::entry::{Entry, PreviewType};
use television_fuzzy::matcher::{config::Config, Matcher}; use television_fuzzy::matcher::{config::Config, Matcher};
use television_utils::strings::preprocess_line;
pub struct Channel { pub struct Channel {
matcher: Matcher<String>, matcher: Matcher<String>,
@ -20,7 +19,7 @@ impl Channel {
let mut lines = Vec::new(); let mut lines = Vec::new();
for line in std::io::stdin().lock().lines().map_while(Result::ok) { for line in std::io::stdin().lock().lines().map_while(Result::ok) {
if !line.trim().is_empty() { if !line.trim().is_empty() {
lines.push(preprocess_line(&line)); lines.push(line);
} }
} }
let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS)); let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS));

View File

@ -11,8 +11,7 @@ use std::{
use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher}; use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher};
use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS}; use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS};
use television_utils::strings::{ use television_utils::strings::{
preprocess_line, proportion_of_printable_ascii_characters, proportion_of_printable_ascii_characters, PRINTABLE_ASCII_THRESHOLD,
PRINTABLE_ASCII_THRESHOLD,
}; };
use tracing::{debug, warn}; use tracing::{debug, warn};
@ -84,7 +83,7 @@ impl Channel {
for entry in entries.into_iter().take(MAX_LINES_IN_MEM) { for entry in entries.into_iter().take(MAX_LINES_IN_MEM) {
injector.push( injector.push(
CandidateLine::new( CandidateLine::new(
entry.display_name().into(), entry.name.into(),
entry.value.unwrap(), entry.value.unwrap(),
entry.line_number.unwrap(), entry.line_number.unwrap(),
), ),
@ -169,15 +168,11 @@ impl OnAir for Channel {
let line = item.matched_string; let line = item.matched_string;
let display_path = let display_path =
item.inner.path.to_string_lossy().to_string(); item.inner.path.to_string_lossy().to_string();
Entry::new( Entry::new(display_path.clone(), PreviewType::Files)
display_path.clone() + &item.inner.line_number.to_string(), .with_value(line)
PreviewType::Files, .with_value_match_ranges(item.match_indices)
) .with_icon(FileIcon::from(item.inner.path.as_path()))
.with_display_name(display_path) .with_line_number(item.inner.line_number)
.with_value(line)
.with_value_match_ranges(item.match_indices)
.with_icon(FileIcon::from(item.inner.path.as_path()))
.with_line_number(item.inner.line_number)
}) })
.collect() .collect()
} }
@ -186,11 +181,6 @@ impl OnAir for Channel {
self.matcher.get_result(index).map(|item| { self.matcher.get_result(index).map(|item| {
let display_path = item.inner.path.to_string_lossy().to_string(); let display_path = item.inner.path.to_string_lossy().to_string();
Entry::new(display_path.clone(), PreviewType::Files) Entry::new(display_path.clone(), PreviewType::Files)
.with_display_name(
display_path.clone()
+ ":"
+ &item.inner.line_number.to_string(),
)
.with_icon(FileIcon::from(item.inner.path.as_path())) .with_icon(FileIcon::from(item.inner.path.as_path()))
.with_line_number(item.inner.line_number) .with_line_number(item.inner.line_number)
}) })
@ -314,8 +304,7 @@ fn try_inject_lines(
match maybe_line { match maybe_line {
Ok(l) => { Ok(l) => {
line_number += 1; line_number += 1;
let line = preprocess_line(&l); if l.is_empty() {
if line.is_empty() {
debug!("Empty line"); debug!("Empty line");
continue; continue;
} }
@ -323,7 +312,7 @@ fn try_inject_lines(
path.strip_prefix(current_dir) path.strip_prefix(current_dir)
.unwrap_or(path) .unwrap_or(path)
.to_path_buf(), .to_path_buf(),
line, l,
line_number, line_number,
); );
let () = injector.push(candidate, |c, cols| { let () = injector.push(candidate, |c, cols| {

View File

@ -10,8 +10,6 @@ use devicons::FileIcon;
pub struct Entry { pub struct Entry {
/// The name of the entry. /// The name of the entry.
pub name: String, pub name: String,
/// The display name of the entry.
display_name: Option<String>,
/// An optional value associated with the entry. /// An optional value associated with the entry.
pub value: Option<String>, pub value: Option<String>,
/// The optional ranges for matching characters in the name. /// The optional ranges for matching characters in the name.
@ -35,7 +33,6 @@ impl Entry {
/// use devicons::FileIcon; /// use devicons::FileIcon;
/// ///
/// let entry = Entry::new("name".to_string(), PreviewType::EnvVar) /// let entry = Entry::new("name".to_string(), PreviewType::EnvVar)
/// .with_display_name("display_name".to_string())
/// .with_value("value".to_string()) /// .with_value("value".to_string())
/// .with_name_match_ranges(vec![(0, 1)]) /// .with_name_match_ranges(vec![(0, 1)])
/// .with_value_match_ranges(vec![(0, 1)]) /// .with_value_match_ranges(vec![(0, 1)])
@ -53,7 +50,6 @@ impl Entry {
pub fn new(name: String, preview_type: PreviewType) -> Self { pub fn new(name: String, preview_type: PreviewType) -> Self {
Self { Self {
name, name,
display_name: None,
value: None, value: None,
name_match_ranges: None, name_match_ranges: None,
value_match_ranges: None, value_match_ranges: None,
@ -63,11 +59,6 @@ impl Entry {
} }
} }
pub fn with_display_name(mut self, display_name: String) -> Self {
self.display_name = Some(display_name);
self
}
pub fn with_value(mut self, value: String) -> Self { pub fn with_value(mut self, value: String) -> Self {
self.value = Some(value); self.value = Some(value);
self self
@ -99,10 +90,6 @@ impl Entry {
self self
} }
pub fn display_name(&self) -> &str {
self.display_name.as_ref().unwrap_or(&self.name)
}
pub fn stdout_repr(&self) -> String { pub fn stdout_repr(&self) -> String {
let mut repr = self.name.clone(); let mut repr = self.name.clone();
if repr.contains(|c| char::is_ascii_whitespace(&c)) { if repr.contains(|c| char::is_ascii_whitespace(&c)) {
@ -118,7 +105,6 @@ impl Entry {
pub const ENTRY_PLACEHOLDER: Entry = Entry { pub const ENTRY_PLACEHOLDER: Entry = Entry {
name: String::new(), name: String::new(),
display_name: None,
value: None, value: None,
name_match_ranges: None, name_match_ranges: None,
value_match_ranges: None, value_match_ranges: None,

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television-derive" name = "television-derive"
version = "0.0.5" version = "0.0.6"
description.workspace = true description.workspace = true
authors.workspace = true authors.workspace = true
repository.workspace = true repository.workspace = true

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television-fuzzy" name = "television-fuzzy"
version = "0.0.5" version = "0.0.6"
description.workspace = true description.workspace = true
authors.workspace = true authors.workspace = true
repository.workspace = true repository.workspace = true

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television-previewers" name = "television-previewers"
version = "0.0.5" version = "0.0.6"
description.workspace = true description.workspace = true
authors.workspace = true authors.workspace = true
repository.workspace = true repository.workspace = true
@ -14,8 +14,8 @@ rust-version.workspace = true
[dependencies] [dependencies]
syntect = "5.2.0" syntect = "5.2.0"
television-channels = { path = "../television-channels", version = "0.0.5" } television-channels = { path = "../television-channels", version = "0.0.6" }
television-utils = { path = "../television-utils", version = "0.0.5" } television-utils = { path = "../television-utils", version = "0.0.6" }
tracing = "0.1.40" tracing = "0.1.40"
parking_lot = "0.12.3" parking_lot = "0.12.3"
tokio = "1.41.1" tokio = "1.41.1"

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use devicons::FileIcon;
use television_channels::entry::{Entry, PreviewType}; use television_channels::entry::{Entry, PreviewType};
pub mod basic; pub mod basic;
@ -46,6 +47,7 @@ pub const FILE_TOO_LARGE_MSG: &str = "File too large";
pub struct Preview { pub struct Preview {
pub title: String, pub title: String,
pub content: PreviewContent, pub content: PreviewContent,
pub icon: Option<FileIcon>,
} }
impl Default for Preview { impl Default for Preview {
@ -53,13 +55,22 @@ impl Default for Preview {
Preview { Preview {
title: String::new(), title: String::new(),
content: PreviewContent::Empty, content: PreviewContent::Empty,
icon: None,
} }
} }
} }
impl Preview { impl Preview {
pub fn new(title: String, content: PreviewContent) -> Self { pub fn new(
Preview { title, content } title: String,
content: PreviewContent,
icon: Option<FileIcon>,
) -> Self {
Preview {
title,
content,
icon,
}
} }
pub fn total_lines(&self) -> u16 { pub fn total_lines(&self) -> u16 {

View File

@ -22,6 +22,7 @@ impl BasicPreviewer {
Arc::new(Preview { Arc::new(Preview {
title: entry.name.clone(), title: entry.name.clone(),
content: PreviewContent::PlainTextWrapped(entry.name.clone()), content: PreviewContent::PlainTextWrapped(entry.name.clone()),
icon: entry.icon,
}) })
} }
} }

View File

@ -58,6 +58,7 @@ fn build_tree_preview(entry: &Entry) -> Preview {
.map(std::borrow::ToOwned::to_owned) .map(std::borrow::ToOwned::to_owned)
.collect(), .collect(),
), ),
icon: entry.icon,
} }
} }

View File

@ -35,6 +35,7 @@ impl EnvVarPreviewer {
} else { } else {
PreviewContent::Empty PreviewContent::Empty
}, },
icon: entry.icon,
}); });
self.cache.insert(entry.clone(), preview.clone()); self.cache.insert(entry.clone(), preview.clone());
preview preview

View File

@ -164,7 +164,7 @@ impl FilePreviewer {
.map_while(Result::ok) .map_while(Result::ok)
// we need to add a newline here because sublime syntaxes expect one // we need to add a newline here because sublime syntaxes expect one
// to be present at the end of each line // to be present at the end of each line
.map(|line| preprocess_line(&line) + "\n") .map(|line| preprocess_line(&line).0 + "\n")
.collect(); .collect();
match syntax::compute_highlights_for_path( match syntax::compute_highlights_for_path(
@ -185,6 +185,7 @@ impl FilePreviewer {
PreviewContent::SyntectHighlightedText( PreviewContent::SyntectHighlightedText(
highlighted_lines, highlighted_lines,
), ),
entry_c.icon,
)), )),
); );
debug!("Inserted highlighted preview into cache"); debug!("Inserted highlighted preview into cache");
@ -224,7 +225,7 @@ fn plain_text_preview(title: &str, reader: BufReader<&File>) -> Arc<Preview> {
// truncate accordingly (since this is just a temp preview) // truncate accordingly (since this is just a temp preview)
for maybe_line in reader.lines() { for maybe_line in reader.lines() {
match maybe_line { match maybe_line {
Ok(line) => lines.push(preprocess_line(&line)), Ok(line) => lines.push(preprocess_line(&line).0),
Err(e) => { Err(e) => {
warn!("Error reading file: {:?}", e); warn!("Error reading file: {:?}", e);
return meta::not_supported(title); return meta::not_supported(title);
@ -237,5 +238,6 @@ fn plain_text_preview(title: &str, reader: BufReader<&File>) -> Arc<Preview> {
Arc::new(Preview::new( Arc::new(Preview::new(
title.to_string(), title.to_string(),
PreviewContent::PlainText(lines), PreviewContent::PlainText(lines),
None,
)) ))
} }

View File

@ -5,6 +5,7 @@ pub fn not_supported(title: &str) -> Arc<Preview> {
Arc::new(Preview::new( Arc::new(Preview::new(
title.to_string(), title.to_string(),
PreviewContent::NotSupported, PreviewContent::NotSupported,
None,
)) ))
} }
@ -12,10 +13,15 @@ pub fn file_too_large(title: &str) -> Arc<Preview> {
Arc::new(Preview::new( Arc::new(Preview::new(
title.to_string(), title.to_string(),
PreviewContent::FileTooLarge, PreviewContent::FileTooLarge,
None,
)) ))
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn loading(title: &str) -> Arc<Preview> { pub fn loading(title: &str) -> Arc<Preview> {
Arc::new(Preview::new(title.to_string(), PreviewContent::Loading)) Arc::new(Preview::new(
title.to_string(),
PreviewContent::Loading,
None,
))
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "television-utils" name = "television-utils"
version = "0.0.5" version = "0.0.6"
description.workspace = true description.workspace = true
authors.workspace = true authors.workspace = true
repository.workspace = true repository.workspace = true

View File

@ -1,5 +1,4 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::fmt::Write;
/// Returns the index of the next character boundary in the given string. /// Returns the index of the next character boundary in the given string.
/// ///
@ -121,7 +120,24 @@ pub fn slice_up_to_char_boundary(s: &str, byte_index: usize) -> &str {
} }
/// Attempts to parse a UTF-8 character from the given byte slice. /// Attempts to parse a UTF-8 character from the given byte slice.
fn try_parse_utf8_char(input: &[u8]) -> Option<(char, usize)> { ///
/// The function returns the parsed character and the number of bytes consumed.
///
/// # Examples
/// ```
/// use television_utils::strings::try_parse_utf8_char;
///
/// let input = b"Hello, World!";
/// let (chr, n) = try_parse_utf8_char(input).unwrap();
/// assert_eq!(chr, 'H');
/// assert_eq!(n, 1);
///
/// let input = b"\xF0\x9F\x91\x8B\xF0\x9F\x8C\x8D!";
/// let (chr, n) = try_parse_utf8_char(input).unwrap();
/// assert_eq!(chr, '👋');
/// assert_eq!(n, 4);
/// ```
pub fn try_parse_utf8_char(input: &[u8]) -> Option<(char, usize)> {
let str_from_utf8 = |seq| std::str::from_utf8(seq).ok(); let str_from_utf8 = |seq| std::str::from_utf8(seq).ok();
let decoded = input let decoded = input
@ -143,7 +159,6 @@ lazy_static! {
pub const EMPTY_STRING: &str = ""; pub const EMPTY_STRING: &str = "";
pub const TAB_WIDTH: usize = 4; pub const TAB_WIDTH: usize = 4;
const SPACE_CHARACTER: char = ' ';
const TAB_CHARACTER: char = '\t'; const TAB_CHARACTER: char = '\t';
const LINE_FEED_CHARACTER: char = '\x0A'; const LINE_FEED_CHARACTER: char = '\x0A';
const DELETE_CHARACTER: char = '\x7F'; const DELETE_CHARACTER: char = '\x7F';
@ -152,62 +167,68 @@ const NULL_CHARACTER: char = '\x00';
const UNIT_SEPARATOR_CHARACTER: char = '\u{001F}'; const UNIT_SEPARATOR_CHARACTER: char = '\u{001F}';
const APPLICATION_PROGRAM_COMMAND_CHARACTER: char = '\u{009F}'; const APPLICATION_PROGRAM_COMMAND_CHARACTER: char = '\u{009F}';
#[allow(clippy::missing_panics_doc)]
/// Replaces non-printable characters in the given byte slice with default printable characters. /// Replaces non-printable characters in the given byte slice with default printable characters.
/// ///
/// The tab width is used to determine how many spaces to replace a tab character with. /// The tab width is used to determine how many spaces to replace a tab character with.
/// The default printable character for non-printable characters is the Unicode symbol for NULL. /// The default printable character for non-printable characters is the Unicode symbol for NULL.
/// ///
/// The function returns a tuple containing the processed string and a vector of offsets introduced
/// by the transformation.
///
/// # Examples /// # Examples
/// ``` /// ```
/// use television_utils::strings::replace_non_printable; /// use television_utils::strings::replace_non_printable;
/// ///
/// let input = b"Hello, World!"; /// let input = b"Hello, World!";
/// let output = replace_non_printable(input, 2); /// let (output, offsets) = replace_non_printable(input, 2);
/// assert_eq!(output, "Hello, World!"); /// assert_eq!(output, "Hello, World!");
/// assert_eq!(offsets, vec![0,0,0,0,0,0,0,0,0,0,0,0,0]);
/// ///
/// let input = b"Hello\tWorld!"; /// let input = b"Hello,\tWorld!";
/// let output = replace_non_printable(input, 2); /// let (output, offsets) = replace_non_printable(input, 4);
/// assert_eq!(output, "Hello World!"); /// assert_eq!(output, "Hello, World!");
/// assert_eq!(offsets, vec![0,0,0,0,0,0,0,3,3,3,3,3,3]);
/// ///
/// let input = b"Hello\nWorld!"; /// let input = b"Hello,\nWorld!";
/// let output = replace_non_printable(input, 2); /// let (output, offsets) = replace_non_printable(input, 2);
/// assert_eq!(output, "HelloWorld!"); /// assert_eq!(output, "Hello,World!");
/// /// assert_eq!(offsets, vec![0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1]);
/// let input = b"Hello\x00World!";
/// let output = replace_non_printable(input, 2);
/// assert_eq!(output, "Hello␀World!");
///
/// let input = b"Hello\x7FWorld!";
/// let output = replace_non_printable(input, 2);
/// assert_eq!(output, "Hello␀World!");
/// ``` /// ```
pub fn replace_non_printable(input: &[u8], tab_width: usize) -> String { pub fn replace_non_printable(
input: &[u8],
tab_width: usize,
) -> (String, Vec<i16>) {
let mut output = String::new(); let mut output = String::new();
let mut offsets = Vec::new();
let mut cumulative_offset: i16 = 0;
let mut idx = 0; let mut idx = 0;
let len = input.len(); let len = input.len();
while idx < len { while idx < len {
offsets.push(cumulative_offset);
if let Some((chr, skip_ahead)) = try_parse_utf8_char(&input[idx..]) { if let Some((chr, skip_ahead)) = try_parse_utf8_char(&input[idx..]) {
idx += skip_ahead; idx += skip_ahead;
match chr { match chr {
// space
SPACE_CHARACTER => output.push(' '),
// tab // tab
TAB_CHARACTER => { TAB_CHARACTER => {
output.push_str(&" ".repeat(tab_width)); output.push_str(&" ".repeat(tab_width));
cumulative_offset += i16::try_from(tab_width).unwrap() - 1;
} }
// line feed // line feed
LINE_FEED_CHARACTER => {} LINE_FEED_CHARACTER => {
cumulative_offset -= 1;
}
// ASCII control characters from 0x00 to 0x1F // ASCII control characters from 0x00 to 0x1F
// + control characters from \u{007F} to \u{009F} // + control characters from \u{007F} to \u{009F}
// + BOM
NULL_CHARACTER..=UNIT_SEPARATOR_CHARACTER NULL_CHARACTER..=UNIT_SEPARATOR_CHARACTER
| DELETE_CHARACTER..=APPLICATION_PROGRAM_COMMAND_CHARACTER => { | DELETE_CHARACTER..=APPLICATION_PROGRAM_COMMAND_CHARACTER
| BOM_CHARACTER => {
output.push(*NULL_SYMBOL); output.push(*NULL_SYMBOL);
} }
// don't print BOMs
BOM_CHARACTER => {}
// Unicode characters above 0x0700 seem unstable with ratatui // Unicode characters above 0x0700 seem unstable with ratatui
c if c > '\u{0700}' => { c if c > '\u{0700}' => {
output.push(*NULL_SYMBOL); output.push(*NULL_SYMBOL);
@ -216,12 +237,12 @@ pub fn replace_non_printable(input: &[u8], tab_width: usize) -> String {
c => output.push(c), c => output.push(c),
} }
} else { } else {
write!(output, "\\x{:02X}", input[idx]).ok(); output.push(*NULL_SYMBOL);
idx += 1; idx += 1;
} }
} }
output (output, offsets)
} }
/// The threshold for considering a buffer to be printable ASCII. /// The threshold for considering a buffer to be printable ASCII.
@ -272,18 +293,21 @@ const MAX_LINE_LENGTH: usize = 300;
/// use television_utils::strings::preprocess_line; /// use television_utils::strings::preprocess_line;
/// ///
/// let line = "Hello, World!"; /// let line = "Hello, World!";
/// let processed = preprocess_line(line); /// let (processed, offsets) = preprocess_line(line);
/// assert_eq!(processed, "Hello, World!"); /// assert_eq!(processed, "Hello, World!");
/// assert_eq!(offsets, vec![0,0,0,0,0,0,0,0,0,0,0,0,0]);
/// ///
/// let line = "\x00World\x7F!"; /// let line = "\x00World\x7F!";
/// let processed = preprocess_line(line); /// let (processed, offsets) = preprocess_line(line);
/// assert_eq!(processed, "␀World␀!"); /// assert_eq!(processed, "␀World␀!");
/// assert_eq!(offsets, vec![0,0,0,0,0,0,0,0]);
/// ///
/// let line = "a".repeat(400); /// let line = "a".repeat(400);
/// let processed = preprocess_line(&line); /// let (processed, offsets) = preprocess_line(&line);
/// assert_eq!(processed.len(), 300); /// assert_eq!(processed.len(), 300);
/// assert_eq!(offsets, vec![0; 300]);
/// ``` /// ```
pub fn preprocess_line(line: &str) -> String { pub fn preprocess_line(line: &str) -> (String, Vec<i16>) {
replace_non_printable( replace_non_printable(
{ {
if line.len() > MAX_LINE_LENGTH { if line.len() > MAX_LINE_LENGTH {
@ -292,12 +316,101 @@ pub fn preprocess_line(line: &str) -> String {
line line
} }
} }
.trim_end_matches(['\r', '\n', '\0'])
.as_bytes(), .as_bytes(),
TAB_WIDTH, TAB_WIDTH,
) )
} }
/// Make a matched string printable while preserving match ranges in the process.
///
/// This function preprocesses the matched string and returns a printable version of it along with
/// the match ranges adjusted to the new string.
///
/// # Examples
/// ```
/// use television_utils::strings::make_matched_string_printable;
///
/// let matched_string = "Hello, World!";
/// let match_ranges = vec![(0, 1), (7, 8)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(matched_string, match_ranges);
/// assert_eq!(printable, "Hello, World!");
/// assert_eq!(match_indices, vec![(0, 1), (7, 8)]);
///
/// let matched_string = "Hello,\tWorld!";
/// let match_ranges = vec![(0, 1), (7, 8)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(matched_string, match_ranges);
/// assert_eq!(printable, "Hello, World!");
/// assert_eq!(match_indices, vec![(0, 1), (10, 11)]);
///
/// let matched_string = "Hello,\nWorld!";
/// let match_ranges = vec![(0, 1), (7, 8)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(matched_string, match_ranges);
/// assert_eq!(printable, "Hello,World!");
/// assert_eq!(match_indices, vec![(0, 1), (6, 7)]);
///
/// let matched_string = "Hello, World!";
/// let (printable, match_indices) = make_matched_string_printable(matched_string, None);
/// assert_eq!(printable, "Hello, World!");
/// assert_eq!(match_indices, vec![]);
///
/// let matched_string = "build.rs";
/// let match_ranges = vec![(0, 1), (7, 8)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(matched_string, match_ranges);
/// assert_eq!(printable, "build.rs");
/// assert_eq!(match_indices, vec![(0, 1), (7, 8)]);
///
/// let matched_string = "a\tb";
/// let match_ranges = vec![(0, 1), (2, 3)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(matched_string, match_ranges);
/// assert_eq!(printable, "a b");
/// assert_eq!(match_indices, vec![(0, 1), (5, 6)]);
///
/// let matched_string = "a\tbcd".repeat(65);
/// let match_ranges = vec![(0, 1), (310, 311)];
/// let match_ranges = Some(match_ranges.as_slice());
/// let (printable, match_indices) = make_matched_string_printable(&matched_string, match_ranges);
/// assert_eq!(printable.len(), 480);
/// assert_eq!(match_indices, vec![(0, 1)]);
/// ```
///
/// # Panics
/// This will panic if the length of the printable string or the match indices don't fit into a
/// `u32`.
pub fn make_matched_string_printable(
matched_string: &str,
match_ranges: Option<&[(u32, u32)]>,
) -> (String, Vec<(u32, u32)>) {
let (printable, transformation_offsets) = preprocess_line(matched_string);
let mut match_indices = Vec::new();
if let Some(ranges) = match_ranges {
for (start, end) in ranges.iter().take_while(|(start, _)| {
*start < u32::try_from(transformation_offsets.len()).unwrap()
}) {
let new_start = i64::from(*start)
+ i64::from(transformation_offsets[*start as usize]);
let new_end = i64::from(*end)
+ i64::from(
// Use the last offset if the end index is out of bounds
// (this will be the case when the match range includes the last character)
transformation_offsets[(*end as usize)
.min(transformation_offsets.len() - 1)],
);
match_indices.push((
u32::try_from(new_start).unwrap(),
u32::try_from(new_end).unwrap(),
));
}
}
(printable, match_indices)
}
/// Shrink a string to a maximum length, adding an ellipsis in the middle. /// Shrink a string to a maximum length, adding an ellipsis in the middle.
/// ///
/// If the string is shorter than the maximum length, it is returned as is. /// If the string is shorter than the maximum length, it is returned as is.
@ -402,7 +515,7 @@ mod tests {
} }
fn test_replace_non_printable(input: &str, expected: &str) { fn test_replace_non_printable(input: &str, expected: &str) {
let actual = replace_non_printable(input.as_bytes(), 2); let (actual, _offset) = replace_non_printable(input.as_bytes(), 2);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -438,7 +551,7 @@ mod tests {
#[test] #[test]
fn test_replace_non_printable_bom() { fn test_replace_non_printable_bom() {
test_replace_non_printable("Hello\u{FEFF}World!", "HelloWorld!"); test_replace_non_printable("Hello\u{FEFF}World!", "HelloWorld!");
} }
#[test] #[test]
@ -446,6 +559,35 @@ mod tests {
test_replace_non_printable("Àì", "Àì␀"); test_replace_non_printable("Àì", "Àì␀");
} }
#[test]
fn test_replace_non_printable_range_tab() {
let input = b"Hello,\tWorld!";
let (output, offsets) = replace_non_printable(input, 4);
assert_eq!(output, "Hello, World!");
assert_eq!(offsets, vec![0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]);
}
#[test]
fn test_replace_non_printable_range_line_feed() {
let input = b"Hello,\nWorld!";
let (output, offsets) = replace_non_printable(input, 2);
assert_eq!(output, "Hello,World!");
assert_eq!(offsets, vec![0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1]);
}
#[test]
fn test_replace_non_printable_no_range_changes() {
let input = b"Hello,\x00World!";
let (output, offsets) = replace_non_printable(input, 2);
assert_eq!(output, "Hello,␀World!");
assert_eq!(offsets, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let input = b"Hello,\x7FWorld!";
let (output, offsets) = replace_non_printable(input, 2);
assert_eq!(output, "Hello,␀World!");
assert_eq!(offsets, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
}
fn test_proportion_of_printable_ascii_characters( fn test_proportion_of_printable_ascii_characters(
input: &str, input: &str,
expected: f32, expected: f32,
@ -469,17 +611,17 @@ mod tests {
} }
fn test_preprocess_line(input: &str, expected: &str) { fn test_preprocess_line(input: &str, expected: &str) {
let actual = preprocess_line(input); let (actual, _offset) = preprocess_line(input);
assert_eq!(actual, expected); assert_eq!(actual, expected, "input: {:?}", input);
} }
#[test] #[test]
fn test_preprocess_line_cases() { fn test_preprocess_line_cases() {
test_preprocess_line("Hello, World!", "Hello, World!"); test_preprocess_line("Hello, World!", "Hello, World!");
test_preprocess_line("Hello, World!\n", "Hello, World!"); test_preprocess_line("Hello, World!\n", "Hello, World!");
test_preprocess_line("Hello, World!\x00", "Hello, World!"); test_preprocess_line("Hello, World!\x00", "Hello, World!");
test_preprocess_line("Hello, World!\x7F", "Hello, World!␀"); test_preprocess_line("Hello, World!\x7F", "Hello, World!␀");
test_preprocess_line("Hello, World!\u{FEFF}", "Hello, World!"); test_preprocess_line("Hello, World!\u{FEFF}", "Hello, World!");
test_preprocess_line(&"a".repeat(400), &"a".repeat(300)); test_preprocess_line(&"a".repeat(400), &"a".repeat(300));
} }
} }

View File

@ -399,15 +399,17 @@ impl Television {
// top right block: preview title // top right block: preview title
self.current_preview_total_lines = preview.total_lines(); self.current_preview_total_lines = preview.total_lines();
self.draw_preview_title_block(f, &layout, &selected_entry, &preview)?; self.draw_preview_title_block(f, &layout, &preview)?;
// bottom right block: preview content // bottom right block: preview content
self.draw_preview_content_block( self.draw_preview_content_block(
f, f,
&layout, &layout,
&selected_entry, selected_entry
.line_number
.map(|l| u16::try_from(l).unwrap_or(0)),
&preview, &preview,
)?; );
// remote control // remote control
if matches!(self.mode, Mode::RemoteControl | Mode::SendToChannel) { if matches!(self.mode, Mode::RemoteControl | Mode::SendToChannel) {

View File

@ -10,7 +10,6 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use syntect::highlighting::Color as SyntectColor; use syntect::highlighting::Color as SyntectColor;
use television_channels::channels::OnAir; use television_channels::channels::OnAir;
use television_channels::entry::Entry;
use television_previewers::previewers::{ use television_previewers::previewers::{
Preview, PreviewContent, FILE_TOO_LARGE_MSG, PREVIEW_NOT_SUPPORTED_MSG, Preview, PreviewContent, FILE_TOO_LARGE_MSG, PREVIEW_NOT_SUPPORTED_MSG,
}; };
@ -28,13 +27,11 @@ impl Television {
&self, &self,
f: &mut Frame, f: &mut Frame,
layout: &Layout, layout: &Layout,
selected_entry: &Entry,
preview: &Arc<Preview>, preview: &Arc<Preview>,
) -> Result<()> { ) -> Result<()> {
let mut preview_title_spans = Vec::new(); let mut preview_title_spans = Vec::new();
if selected_entry.icon.is_some() && self.config.ui.use_nerd_font_icons if preview.icon.is_some() && self.config.ui.use_nerd_font_icons {
{ let icon = preview.icon.as_ref().unwrap();
let icon = selected_entry.icon.as_ref().unwrap();
preview_title_spans.push(Span::styled( preview_title_spans.push(Span::styled(
{ {
let mut icon_str = String::from(icon.icon); let mut icon_str = String::from(icon.icon);
@ -68,9 +65,9 @@ impl Television {
&mut self, &mut self,
f: &mut Frame, f: &mut Frame,
layout: &Layout, layout: &Layout,
selected_entry: &Entry, target_line: Option<u16>,
preview: &Arc<Preview>, preview: &Arc<Preview>,
) -> Result<()> { ) {
let preview_outer_block = Block::default() let preview_outer_block = Block::default()
.title_top(Line::from(" Preview ").alignment(Alignment::Center)) .title_top(Line::from(" Preview ").alignment(Alignment::Center))
.borders(Borders::ALL) .borders(Borders::ALL)
@ -101,13 +98,10 @@ impl Television {
preview_inner_block, preview_inner_block,
inner, inner,
preview, preview,
selected_entry target_line,
.line_number
.map(|l| u16::try_from(l).unwrap_or(0)),
); );
f.render_widget(preview_block, inner); f.render_widget(preview_block, inner);
//} //}
Ok(())
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -209,7 +203,7 @@ impl Television {
.block(preview_block) .block(preview_block)
.alignment(Alignment::Left) .alignment(Alignment::Left)
.style(Style::default().add_modifier(Modifier::ITALIC)), .style(Style::default().add_modifier(Modifier::ITALIC)),
_ => Paragraph::new(Text::raw(EMPTY_STRING)), PreviewContent::Empty => Paragraph::new(Text::raw(EMPTY_STRING)),
} }
} }
@ -317,7 +311,7 @@ fn compute_paragraph_from_highlighted_lines(
let line_number = let line_number =
build_line_number_span(i + 1).style(Style::default().fg( build_line_number_span(i + 1).style(Style::default().fg(
if line_specifier.is_some() if line_specifier.is_some()
&& i == line_specifier.unwrap() - 1 && i == line_specifier.unwrap().saturating_sub(1)
{ {
DEFAULT_PREVIEW_GUTTER_SELECTED_FG DEFAULT_PREVIEW_GUTTER_SELECTED_FG
} else { } else {
@ -334,7 +328,9 @@ fn compute_paragraph_from_highlighted_lines(
convert_syn_region_to_span( convert_syn_region_to_span(
&(sr.0, sr.1), &(sr.0, sr.1),
if line_specifier.is_some() if line_specifier.is_some()
&& i == line_specifier.unwrap() - 1 && i == line_specifier
.unwrap()
.saturating_sub(1)
{ {
Some(SyntectColor { Some(SyntectColor {
r: 50, r: 50,

View File

@ -12,7 +12,8 @@ use std::str::FromStr;
use television_channels::channels::OnAir; use television_channels::channels::OnAir;
use television_channels::entry::Entry; use television_channels::entry::Entry;
use television_utils::strings::{ use television_utils::strings::{
next_char_boundary, slice_at_char_boundaries, make_matched_string_printable, next_char_boundary,
slice_at_char_boundaries,
}; };
// Styles // Styles
@ -76,45 +77,41 @@ where
List::new(entries.iter().map(|entry| { List::new(entries.iter().map(|entry| {
let mut spans = Vec::new(); let mut spans = Vec::new();
// optional icon // optional icon
if entry.icon.is_some() && use_icons { if let Some(icon) = entry.icon.as_ref() {
let icon = entry.icon.as_ref().unwrap(); if use_icons {
spans.push(Span::styled( spans.push(Span::styled(
icon.to_string(), icon.to_string(),
Style::default().fg(Color::from_str(icon.color).unwrap()), Style::default().fg(Color::from_str(icon.color).unwrap()),
)); ));
spans.push(Span::raw(" ")); spans.push(Span::raw(" "));
}
} }
// entry name // entry name
if let Some(name_match_ranges) = &entry.name_match_ranges { let (entry_name, name_match_ranges) = make_matched_string_printable(
let mut last_match_end = 0; &entry.name,
for (start, end) in name_match_ranges entry.name_match_ranges.as_deref(),
.iter() );
.map(|(s, e)| (*s as usize, *e as usize)) let mut last_match_end = 0;
{ for (start, end) in name_match_ranges
spans.push(Span::styled( .iter()
slice_at_char_boundaries( .map(|(s, e)| (*s as usize, *e as usize))
&entry.name, {
last_match_end,
start,
),
Style::default().fg(results_list_colors.result_name_fg),
));
spans.push(Span::styled(
slice_at_char_boundaries(&entry.name, start, end),
Style::default().fg(Color::Red),
));
last_match_end = end;
}
spans.push(Span::styled( spans.push(Span::styled(
&entry.name[next_char_boundary(&entry.name, last_match_end)..], slice_at_char_boundaries(&entry_name, last_match_end, start)
.to_string(),
Style::default().fg(results_list_colors.result_name_fg), Style::default().fg(results_list_colors.result_name_fg),
)); ));
} else {
spans.push(Span::styled( spans.push(Span::styled(
entry.display_name(), slice_at_char_boundaries(&entry_name, start, end).to_string(),
Style::default().fg(results_list_colors.result_name_fg), Style::default().fg(Color::Red),
)); ));
last_match_end = end;
} }
spans.push(Span::styled(
entry_name[next_char_boundary(&entry_name, last_match_end)..]
.to_string(),
Style::default().fg(results_list_colors.result_name_fg),
));
// optional line number // optional line number
if let Some(line_number) = entry.line_number { if let Some(line_number) = entry.line_number {
spans.push(Span::styled( spans.push(Span::styled(
@ -126,43 +123,32 @@ where
if let Some(preview) = &entry.value { if let Some(preview) = &entry.value {
spans.push(Span::raw(": ")); spans.push(Span::raw(": "));
if let Some(preview_match_ranges) = &entry.value_match_ranges { let (preview, preview_match_ranges) =
if !preview_match_ranges.is_empty() { make_matched_string_printable(
let mut last_match_end = 0;
for (start, end) in preview_match_ranges
.iter()
.map(|(s, e)| (*s as usize, *e as usize))
{
spans.push(Span::styled(
slice_at_char_boundaries(
preview,
last_match_end,
start,
),
Style::default()
.fg(results_list_colors.result_preview_fg),
));
spans.push(Span::styled(
slice_at_char_boundaries(preview, start, end),
Style::default().fg(Color::Red),
));
last_match_end = end;
}
spans.push(Span::styled(
&preview[next_char_boundary(
preview,
preview_match_ranges.last().unwrap().1 as usize,
)..],
Style::default()
.fg(results_list_colors.result_preview_fg),
));
}
} else {
spans.push(Span::styled(
preview, preview,
entry.value_match_ranges.as_deref(),
);
let mut last_match_end = 0;
for (start, end) in preview_match_ranges
.iter()
.map(|(s, e)| (*s as usize, *e as usize))
{
spans.push(Span::styled(
slice_at_char_boundaries(&preview, last_match_end, start)
.to_string(),
Style::default().fg(results_list_colors.result_preview_fg), Style::default().fg(results_list_colors.result_preview_fg),
)); ));
spans.push(Span::styled(
slice_at_char_boundaries(&preview, start, end).to_string(),
Style::default().fg(Color::Red),
));
last_match_end = end;
} }
spans.push(Span::styled(
preview[next_char_boundary(&preview, last_match_end)..]
.to_string(),
Style::default().fg(results_list_colors.result_preview_fg),
));
} }
Line::from(spans) Line::from(spans)
})) }))