mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: dynamic previewer Lua API (#3891)
This commit is contained in:
parent
80a54c3dd5
commit
463150848d
186 changed files with 2610 additions and 1598 deletions
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
|
|
@ -51,7 +51,7 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: JohnnyMorganz/stylua-action@v4
|
- uses: JohnnyMorganz/stylua-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
version: latest
|
version: latest
|
||||||
|
|
|
||||||
4
.github/workflows/draft.yml
vendored
4
.github/workflows/draft.yml
vendored
|
|
@ -210,7 +210,7 @@ jobs:
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Draft
|
- name: Draft
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
|
|
@ -246,7 +246,7 @@ jobs:
|
||||||
git tag --force nightly && git push --force origin tag nightly
|
git tag --force nightly && git push --force origin tag nightly
|
||||||
|
|
||||||
- name: Nightly
|
- name: Nightly
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
tag_name: nightly
|
tag_name: nightly
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|
|
||||||
2
.github/workflows/validate-form.yml
vendored
2
.github/workflows/validate-form.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
npm ci
|
npm ci
|
||||||
|
|
||||||
- name: Validate Form
|
- name: Validate Form
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v9
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const script = require('./scripts/validate-form/main.js')
|
const script = require('./scripts/validate-form/main.js')
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
- New `--in` for `search` action to set search directory ([#3696])
|
- New `--in` for `search` action to set search directory ([#3696])
|
||||||
- Hover cursor over the new file after copying/cutting/linking/hardlinking/extracting ([#3846], [#3854])
|
- Hover cursor over the new file after copying/cutting/linking/hardlinking/extracting ([#3846], [#3854])
|
||||||
- Multi-file spotter ([#3733])
|
- Multi-file spotter ([#3733])
|
||||||
|
- Dynamic previewer Lua API ([#3891])
|
||||||
- Vim-like `lua` action that runs an inline Lua snippet ([#3813])
|
- Vim-like `lua` action that runs an inline Lua snippet ([#3813])
|
||||||
- Certificate authentication for SFTP VFS provider ([#3716])
|
- Certificate authentication for SFTP VFS provider ([#3716])
|
||||||
- New `hovered` condition specifying different icons for hovered files ([#3728])
|
- New `hovered` condition specifying different icons for hovered files ([#3728])
|
||||||
|
|
@ -1699,3 +1700,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
[#3846]: https://github.com/sxyazi/yazi/pull/3846
|
[#3846]: https://github.com/sxyazi/yazi/pull/3846
|
||||||
[#3854]: https://github.com/sxyazi/yazi/pull/3854
|
[#3854]: https://github.com/sxyazi/yazi/pull/3854
|
||||||
[#3862]: https://github.com/sxyazi/yazi/pull/3862
|
[#3862]: https://github.com/sxyazi/yazi/pull/3862
|
||||||
|
[#3891]: https://github.com/sxyazi/yazi/pull/3891
|
||||||
|
|
|
||||||
195
Cargo.lock
generated
195
Cargo.lock
generated
|
|
@ -211,6 +211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
|
checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustversion",
|
"rustversion",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -410,20 +411,20 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.0"
|
version = "2.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitstream-io"
|
name = "bitstream-io"
|
||||||
version = "4.9.0"
|
version = "4.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
|
checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core2",
|
"no_std_io2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -629,7 +630,7 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures 0.3.0",
|
"cpufeatures 0.3.0",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -669,9 +670,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.6.0"
|
version = "4.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -691,9 +692,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.6.1"
|
version = "4.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "406e68b4de5c59cfb8f750a7cbd4d31ae153788b8352167c1e5f4fc26e8c91e9"
|
checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
@ -720,9 +721,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.6.0"
|
version = "4.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
|
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
@ -826,15 +827,6 @@ version = "0.8.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core2"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpubits"
|
name = "cpubits"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -908,7 +900,7 @@ version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
|
@ -925,7 +917,7 @@ version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"document-features",
|
"document-features",
|
||||||
|
|
@ -967,7 +959,7 @@ dependencies = [
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"serdect",
|
"serdect",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
|
@ -991,7 +983,7 @@ checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1002,7 +994,7 @@ checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"libm",
|
"libm",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1349,7 +1341,7 @@ checksum = "053618a4c3d3bc24f188aa660ae75a46eeab74ef07fb415c61431e5e7cd4749b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"ed25519",
|
"ed25519",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"signature",
|
"signature",
|
||||||
|
|
@ -1378,7 +1370,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pem-rfc7468 1.0.0",
|
"pem-rfc7468 1.0.0",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"rustcrypto-ff",
|
"rustcrypto-ff",
|
||||||
"rustcrypto-group",
|
"rustcrypto-group",
|
||||||
"sec1",
|
"sec1",
|
||||||
|
|
@ -1767,7 +1759,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 6.0.0",
|
"r-efi 6.0.0",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasip3",
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
@ -2046,7 +2038,7 @@ version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199"
|
checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"inotify-sys",
|
"inotify-sys",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
@ -2110,7 +2102,7 @@ dependencies = [
|
||||||
"p256",
|
"p256",
|
||||||
"p384",
|
"p384",
|
||||||
"p521",
|
"p521",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"rsa",
|
"rsa",
|
||||||
"sec1",
|
"sec1",
|
||||||
"sha1 0.11.0",
|
"sha1 0.11.0",
|
||||||
|
|
@ -2130,8 +2122,8 @@ checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2216,7 +2208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
|
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-common 0.2.1",
|
"crypto-common 0.2.1",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2268,9 +2260,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.184"
|
version = "0.2.185"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
|
|
@ -2303,7 +2295,7 @@ version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
|
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2350,13 +2342,22 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e0b564323a0fb6d54b864f625ae139de9612e27edb944dda37c109f05aac531"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.17.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lua-src"
|
name = "lua-src"
|
||||||
version = "550.0.0"
|
version = "550.0.0"
|
||||||
|
|
@ -2468,7 +2469,7 @@ dependencies = [
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"kem",
|
"kem",
|
||||||
"module-lattice",
|
"module-lattice",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"sha3",
|
"sha3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2556,7 +2557,7 @@ version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -2569,12 +2570,21 @@ version = "0.31.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "no_std_io2"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
|
@ -2606,7 +2616,7 @@ version = "8.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"fsevent-sys",
|
"fsevent-sys",
|
||||||
"inotify",
|
"inotify",
|
||||||
"kqueue",
|
"kqueue",
|
||||||
|
|
@ -2624,7 +2634,7 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
|
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2766,7 +2776,7 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2797,7 +2807,7 @@ version = "6.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
|
checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"onig_sys",
|
"onig_sys",
|
||||||
|
|
@ -3156,7 +3166,7 @@ dependencies = [
|
||||||
"cbc 0.2.0",
|
"cbc 0.2.0",
|
||||||
"der",
|
"der",
|
||||||
"pbkdf2 0.13.0-rc.10",
|
"pbkdf2 0.13.0-rc.10",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"scrypt",
|
"scrypt",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"spki",
|
"spki",
|
||||||
|
|
@ -3170,15 +3180,15 @@ checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"pkcs5",
|
"pkcs5",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plist"
|
name = "plist"
|
||||||
|
|
@ -3199,7 +3209,7 @@ version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"fdeflate",
|
"fdeflate",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
|
@ -3279,7 +3289,7 @@ checksum = "c6543f5eec854fbf74ba5ef651fbdc9408919b47c3e1526623687135c16d12e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"crypto-common 0.2.1",
|
"crypto-common 0.2.1",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"rustcrypto-ff",
|
"rustcrypto-ff",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
|
@ -3431,9 +3441,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.2"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rand_core 0.9.5",
|
"rand_core 0.9.5",
|
||||||
|
|
@ -3441,13 +3451,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20 0.10.0",
|
"chacha20 0.10.0",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3491,9 +3501,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
|
|
@ -3517,13 +3527,13 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
|
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"indoc",
|
"indoc",
|
||||||
"itertools",
|
"itertools",
|
||||||
"kasuari",
|
"kasuari",
|
||||||
"lru",
|
"lru 0.16.4",
|
||||||
"serde",
|
"serde",
|
||||||
"strum 0.27.2",
|
"strum 0.27.2",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
|
@ -3582,7 +3592,7 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
|
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"indoc",
|
"indoc",
|
||||||
"instability",
|
"instability",
|
||||||
|
|
@ -3623,7 +3633,7 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"paste",
|
"paste",
|
||||||
"profiling",
|
"profiling",
|
||||||
"rand 0.9.2",
|
"rand 0.9.4",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"simd_helpers",
|
"simd_helpers",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
|
@ -3647,9 +3657,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
|
|
@ -3671,7 +3681,7 @@ version = "0.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3776,7 +3786,7 @@ dependencies = [
|
||||||
"digest 0.11.2",
|
"digest 0.11.2",
|
||||||
"pkcs1",
|
"pkcs1",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"signature",
|
"signature",
|
||||||
"spki",
|
"spki",
|
||||||
|
|
@ -3790,7 +3800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b530252dc3ff163b73a7e48c97b925450d2ca53edcb466a46ad0a231e45f998"
|
checksum = "3b530252dc3ff163b73a7e48c97b925450d2ca53edcb466a46ad0a231e45f998"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.8.4",
|
"aes 0.8.4",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"block-padding 0.3.3",
|
"block-padding 0.3.3",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -3828,8 +3838,8 @@ dependencies = [
|
||||||
"pkcs5",
|
"pkcs5",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"polyval 0.7.1",
|
"polyval 0.7.1",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"ring",
|
"ring",
|
||||||
"rsa",
|
"rsa",
|
||||||
"russh-cryptovec",
|
"russh-cryptovec",
|
||||||
|
|
@ -3899,7 +3909,7 @@ version = "0.14.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6"
|
checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -3909,7 +3919,7 @@ version = "0.14.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed"
|
checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
"rustcrypto-ff",
|
"rustcrypto-ff",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
@ -3920,7 +3930,7 @@ version = "0.38.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.15",
|
"linux-raw-sys 0.4.15",
|
||||||
|
|
@ -3933,7 +3943,7 @@ version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.12.1",
|
"linux-raw-sys 0.12.1",
|
||||||
|
|
@ -4274,7 +4284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3"
|
checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest 0.11.2",
|
"digest 0.11.2",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4525,7 +4535,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"filedescriptor",
|
"filedescriptor",
|
||||||
"finl_unicode",
|
"finl_unicode",
|
||||||
|
|
@ -4678,9 +4688,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.51.1"
|
version = "1.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -4864,7 +4874,7 @@ version = "2.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.9.2",
|
"rand 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4966,9 +4976,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.23.0"
|
version = "1.23.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic",
|
"atomic",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
|
|
@ -5175,7 +5185,7 @@ version = "0.244.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"hashbrown 0.15.5",
|
"hashbrown 0.15.5",
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
"semver",
|
"semver",
|
||||||
|
|
@ -5703,7 +5713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -5908,7 +5918,8 @@ name = "yazi-config"
|
||||||
version = "26.2.2"
|
version = "26.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.11.0",
|
"arc-swap",
|
||||||
|
"bitflags 2.11.1",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
"globset",
|
"globset",
|
||||||
"hashbrown 0.17.0",
|
"hashbrown 0.17.0",
|
||||||
|
|
@ -5916,6 +5927,7 @@ dependencies = [
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_with",
|
||||||
"strum 0.28.0",
|
"strum 0.28.0",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
|
@ -5924,6 +5936,7 @@ dependencies = [
|
||||||
"yazi-fs",
|
"yazi-fs",
|
||||||
"yazi-macro",
|
"yazi-macro",
|
||||||
"yazi-shared",
|
"yazi-shared",
|
||||||
|
"yazi-shim",
|
||||||
"yazi-tty",
|
"yazi-tty",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -6066,7 +6079,7 @@ version = "26.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"dirs",
|
"dirs",
|
||||||
"either",
|
"either",
|
||||||
|
|
@ -6076,7 +6089,7 @@ dependencies = [
|
||||||
"objc2",
|
"objc2",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"regex",
|
"regex",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -6106,7 +6119,7 @@ name = "yazi-parser"
|
||||||
version = "26.2.2"
|
version = "26.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
"hashbrown 0.17.0",
|
"hashbrown 0.17.0",
|
||||||
"mlua",
|
"mlua",
|
||||||
|
|
@ -6217,7 +6230,7 @@ dependencies = [
|
||||||
"foldhash 0.2.0",
|
"foldhash 0.2.0",
|
||||||
"hashbrown 0.17.0",
|
"hashbrown 0.17.0",
|
||||||
"libc",
|
"libc",
|
||||||
"lru",
|
"lru 0.17.0",
|
||||||
"mlua",
|
"mlua",
|
||||||
"ordered-float 5.3.0",
|
"ordered-float 5.3.0",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
|
@ -6240,7 +6253,7 @@ dependencies = [
|
||||||
name = "yazi-sftp"
|
name = "yazi-sftp"
|
||||||
version = "26.2.2"
|
version = "26.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.1",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"russh",
|
"russh",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -6278,8 +6291,12 @@ dependencies = [
|
||||||
name = "yazi-shim"
|
name = "yazi-shim"
|
||||||
version = "26.2.2"
|
version = "26.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"serde",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"toml",
|
||||||
"twox-hash",
|
"twox-hash",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
|
|
||||||
14
Cargo.toml
14
Cargo.toml
|
|
@ -37,11 +37,11 @@ debug = false
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
ansi-to-tui = "8.0.1"
|
ansi-to-tui = "8.0.1"
|
||||||
anyhow = "1.0.102"
|
anyhow = "1.0.102"
|
||||||
arc-swap = "1.9.1"
|
arc-swap = { version = "1.9.1", features = [ "serde" ] }
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bitflags = { version = "2.11.0", features = [ "serde" ] }
|
bitflags = { version = "2.11.1", features = [ "serde" ] }
|
||||||
chrono = "0.4.44"
|
chrono = "0.4.44"
|
||||||
clap = { version = "4.6.0", features = [ "derive" ] }
|
clap = { version = "4.6.1", features = [ "derive" ] }
|
||||||
core-foundation-sys = "0.8.7"
|
core-foundation-sys = "0.8.7"
|
||||||
crossterm = { version = "0.29.0", features = [ "event-stream" ] }
|
crossterm = { version = "0.29.0", features = [ "event-stream" ] }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
|
@ -52,15 +52,15 @@ futures = "0.3.32"
|
||||||
globset = "0.4.18"
|
globset = "0.4.18"
|
||||||
hashbrown = { version = "0.17.0", features = [ "serde" ] }
|
hashbrown = { version = "0.17.0", features = [ "serde" ] }
|
||||||
indexmap = { version = "2.14.0", features = [ "serde" ] }
|
indexmap = { version = "2.14.0", features = [ "serde" ] }
|
||||||
libc = "0.2.184"
|
libc = "0.2.185"
|
||||||
lru = "0.16.3"
|
lru = "0.17.0"
|
||||||
mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] }
|
mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] }
|
||||||
objc2 = "0.6.4"
|
objc2 = "0.6.4"
|
||||||
ordered-float = { version = "5.3.0", features = [ "serde" ] }
|
ordered-float = { version = "5.3.0", features = [ "serde" ] }
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
percent-encoding = "2.3.2"
|
percent-encoding = "2.3.2"
|
||||||
rand = { version = "0.10.0", default-features = false, features = [ "std", "sys_rng" ] }
|
rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] }
|
||||||
ratatui = { version = "0.30.0", features = [ "serde", "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
ratatui = { version = "0.30.0", features = [ "serde", "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
||||||
regex = "1.12.3"
|
regex = "1.12.3"
|
||||||
russh = { version = "0.60.0", default-features = false, features = [ "ring", "rsa" ] }
|
russh = { version = "0.60.0", default-features = false, features = [ "ring", "rsa" ] }
|
||||||
|
|
@ -71,7 +71,7 @@ serde_with = "3.18.0"
|
||||||
strum = { version = "0.28.0", features = [ "derive" ] }
|
strum = { version = "0.28.0", features = [ "derive" ] }
|
||||||
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
tokio = { version = "1.51.1", features = [ "full" ] }
|
tokio = { version = "1.52.0", features = [ "full" ] }
|
||||||
tokio-stream = "0.1.18"
|
tokio-stream = "0.1.18"
|
||||||
tokio-util = "0.7.18"
|
tokio-util = "0.7.18"
|
||||||
toml = { version = "1.1.2" }
|
toml = { version = "1.1.2" }
|
||||||
|
|
|
||||||
40
README.md
40
README.md
|
|
@ -1,8 +1,8 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<sup>Special thanks to:</sup><br>
|
<sup>Special thanks to:</sup><br>
|
||||||
|
|
||||||
|<a href="https://go.warp.dev/yazi" target="_blank"><img alt="Warp sponsorship" width=350 src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png"><br><b>Warp, built for coding with multiple AI agents</b><br><sup>Available for macOS, Linux and Windows</sup></a>|
|
| <a href="https://go.warp.dev/yazi" target="_blank"><img alt="Warp sponsorship" width=350 src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png"><br><b>Warp, built for coding with multiple AI agents</b><br><sup>Available for macOS, Linux and Windows</sup></a> |
|
||||||
|-|
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -47,24 +47,24 @@ Yazi is currently in heavy development, expect breaking changes.
|
||||||
|
|
||||||
## Image Preview
|
## Image Preview
|
||||||
|
|
||||||
| Platform | Protocol | Support |
|
| Platform | Protocol | Support |
|
||||||
| ---------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------- |
|
| ---------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------- |
|
||||||
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||||
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
|
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
|
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
|
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
|
||||||
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
|
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||||
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||||
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
|
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||||
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
|
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||||
| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in |
|
| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio renders images at incorrect sizes |
|
| [Rio](https://github.com/raphamorim/rio) (>= 0.3.9) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||||
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
|
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
|
||||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
||||||
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required (>= 1.16.0) |
|
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required (>= 1.16.0) |
|
||||||
|
|
||||||
See https://yazi-rs.github.io/docs/image-preview for details.
|
See https://yazi-rs.github.io/docs/image-preview for details.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr"],"flagWords":[],"language":"en","version":"0.2"}
|
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee"],"version":"0.2","language":"en","flagWords":[]}
|
||||||
8
scripts/validate-form/package-lock.json
generated
8
scripts/validate-form/package-lock.json
generated
|
|
@ -9,7 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.0",
|
"@actions/core": "^3.0.0",
|
||||||
"@actions/github": "^9.0.0"
|
"@actions/github": "^9.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
|
|
@ -42,9 +42,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/github": {
|
"node_modules/@actions/github": {
|
||||||
"version": "9.0.0",
|
"version": "9.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/github/-/github-9.1.0.tgz",
|
||||||
"integrity": "sha512-yJ0RoswsAaKcvkmpCE4XxBRiy/whH2SdTBHWzs0gi4wkqTDhXMChjSdqBz/F4AeiDlP28rQqL33iHb+kjAMX6w==",
|
"integrity": "sha512-u0hDGQeCS+7VNoLA8hYG65RLdPLMaPGfka0sZ0up7P0AiShqfX6xcuXNteGkQ7X7Tod7AMNwHd4p7DS63i8zzA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/http-client": "^3.0.2",
|
"@actions/http-client": "^3.0.2",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.0",
|
"@actions/core": "^3.0.0",
|
||||||
"@actions/github": "^9.0.0"
|
"@actions/github": "^9.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ impl DerefMut for Ctx<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Ctx<'a> {
|
impl<'a> Ctx<'a> {
|
||||||
#[inline]
|
|
||||||
pub fn new(action: &Action, core: &'a mut Core, term: &'a mut Option<Term>) -> Result<Self> {
|
pub fn new(action: &Action, core: &'a mut Core, term: &'a mut Option<Term>) -> Result<Self> {
|
||||||
let tab = if let Ok(id) = action.get::<Id>("tab") {
|
let tab = if let Ok(id) = action.get::<Id>("tab") {
|
||||||
core
|
core
|
||||||
|
|
@ -51,7 +50,6 @@ impl<'a> Ctx<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn renew<'b>(cx: &'a mut Ctx<'b>) -> Self {
|
pub fn renew<'b>(cx: &'a mut Ctx<'b>) -> Self {
|
||||||
let tab = cx.core.mgr.tabs.cursor;
|
let tab = cx.core.mgr.tabs.cursor;
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -65,7 +63,6 @@ impl<'a> Ctx<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn active(core: &'a mut Core, term: &'a mut Option<Term>) -> Self {
|
pub fn active(core: &'a mut Core, term: &'a mut Option<Term>) -> Self {
|
||||||
let tab = core.mgr.tabs.cursor;
|
let tab = core.mgr.tabs.cursor;
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -119,6 +116,5 @@ impl<'a> Ctx<'a> {
|
||||||
self.tab_mut().hovered_folder_mut()
|
self.tab_mut().hovered_folder_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } }
|
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{ops::Deref, ptr};
|
||||||
|
|
||||||
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value};
|
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value};
|
||||||
use yazi_binding::{Range, Style, cached_field};
|
use yazi_binding::{Range, Style, cached_field};
|
||||||
use yazi_config::THEME;
|
use yazi_config::{Selectable, THEME};
|
||||||
use yazi_shared::{path::AsPath, url::UrlLike};
|
use yazi_shared::{path::AsPath, url::UrlLike};
|
||||||
|
|
||||||
use super::Lives;
|
use super::Lives;
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ impl BulkRename {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opener() -> Option<&'static OpenerRule> {
|
fn opener() -> Option<&'static OpenerRule> {
|
||||||
YAZI.opener.block(YAZI.open.all(Path::new("bulk-rename.txt"), "text/plain"))
|
YAZI.opener.block(YAZI.open.all_dummy(Path::new("bulk-rename.txt"), "text/plain"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_url(url: &UrlBuf, take: usize, rep: &StrandBuf) -> Result<UrlBuf> {
|
fn replace_url(url: &UrlBuf, take: usize, rep: &StrandBuf) -> Result<UrlBuf> {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}};
|
||||||
use yazi_macro::succ;
|
use yazi_macro::succ;
|
||||||
use yazi_parser::mgr::DownloadForm;
|
use yazi_parser::mgr::DownloadForm;
|
||||||
use yazi_proxy::MgrProxy;
|
use yazi_proxy::MgrProxy;
|
||||||
use yazi_shared::{data::Data, url::{UrlCow, UrlLike}};
|
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
|
||||||
use yazi_vfs::VfsFile;
|
use yazi_vfs::VfsFile;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -29,7 +29,7 @@ impl Actor for Download {
|
||||||
|
|
||||||
let mut wg1 = FuturesUnordered::new();
|
let mut wg1 = FuturesUnordered::new();
|
||||||
for url in form.urls {
|
for url in form.urls {
|
||||||
let done = scheduler.file_download(url.to_owned());
|
let done = scheduler.file_download(url.clone());
|
||||||
wg1.push(async move { (done.future().await, url) });
|
wg1.push(async move { (done.future().await, url) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ impl Actor for Download {
|
||||||
}
|
}
|
||||||
if form.open && !urls.is_empty() {
|
if form.open && !urls.is_empty() {
|
||||||
MgrProxy::open(OpenOpt {
|
MgrProxy::open(OpenOpt {
|
||||||
cwd: Some(cwd.into()),
|
cwd: Some(cwd),
|
||||||
targets: urls,
|
targets: urls,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
hovered: false,
|
hovered: false,
|
||||||
|
|
@ -73,7 +73,7 @@ impl Actor for Download {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Download {
|
impl Download {
|
||||||
async fn prepare(urls: &[UrlCow<'_>]) {
|
async fn prepare(urls: &[UrlBuf]) {
|
||||||
let roots: HashSet<_> = urls.iter().filter_map(|u| u.scheme().cache()).collect();
|
let roots: HashSet<_> = urls.iter().filter_map(|u| u.scheme().cache()).collect();
|
||||||
for mut root in roots {
|
for mut root in roots {
|
||||||
root.push("%lock");
|
root.push("%lock");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use hashbrown::HashSet;
|
||||||
use yazi_boot::ARGS;
|
use yazi_boot::ARGS;
|
||||||
use yazi_core::mgr::OpenDoOpt;
|
use yazi_core::mgr::OpenDoOpt;
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
|
|
@ -31,17 +33,17 @@ impl Actor for Open {
|
||||||
|
|
||||||
if opt.targets.is_empty() {
|
if opt.targets.is_empty() {
|
||||||
opt.targets = if opt.hovered {
|
opt.targets = if opt.hovered {
|
||||||
cx.hovered().map(|h| vec![h.url.clone().into()]).unwrap_or_default()
|
cx.hovered().map(|h| vec![h.url.clone()]).unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
act!(mgr:escape_visual, cx)?;
|
act!(mgr:escape_visual, cx)?;
|
||||||
cx.tab().selected_or_hovered().cloned().map(Into::into).collect()
|
cx.tab().selected_or_hovered().cloned().collect()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if opt.targets.is_empty() {
|
if opt.targets.is_empty() {
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
let todo: Vec<_> = opt
|
let todo: HashSet<_> = opt
|
||||||
.targets
|
.targets
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -49,21 +51,28 @@ impl Actor for Open {
|
||||||
.map(|(i, _)| i)
|
.map(|(i, _)| i)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let cwd = opt.cwd.unwrap_or_else(|| cx.cwd().clone().into());
|
let cwd = opt.cwd.unwrap_or_else(|| cx.cwd().clone());
|
||||||
if todo.is_empty() {
|
|
||||||
return act!(mgr:open_do, cx, OpenDoOpt { cwd, targets: opt.targets, interactive: opt.interactive });
|
|
||||||
}
|
|
||||||
|
|
||||||
let scheduler = cx.tasks.scheduler.clone();
|
let scheduler = cx.tasks.scheduler.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut files = Vec::with_capacity(todo.len());
|
let mut all = Vec::with_capacity(opt.targets.len());
|
||||||
for i in todo {
|
let mut part = Vec::with_capacity(todo.len());
|
||||||
if let Ok(f) = File::new(&opt.targets[i]).await {
|
|
||||||
files.push(f);
|
let it = futures::stream::iter(opt.targets)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, url)| async move { File::new(url).await.ok().map(|file| (i, file)) })
|
||||||
|
.buffered(3)
|
||||||
|
.filter_map(|item| async move { item });
|
||||||
|
|
||||||
|
futures::pin_mut!(it);
|
||||||
|
while let Some((i, file)) = it.next().await {
|
||||||
|
if todo.contains(&i) {
|
||||||
|
part.push(file.clone());
|
||||||
}
|
}
|
||||||
|
all.push(file);
|
||||||
}
|
}
|
||||||
if scheduler.fetch_mimetype(files).await {
|
|
||||||
MgrProxy::open_do(OpenDoOpt { cwd, targets: opt.targets, interactive: opt.interactive });
|
if !all.is_empty() && scheduler.fetch_mimetype(part).await {
|
||||||
|
MgrProxy::open_do(OpenDoOpt { cwd, targets: all, interactive: opt.interactive });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
succ!();
|
succ!();
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use yazi_macro::succ;
|
||||||
use yazi_parser::mgr::OpenDoForm;
|
use yazi_parser::mgr::OpenDoForm;
|
||||||
use yazi_proxy::{PickProxy, TasksProxy};
|
use yazi_proxy::{PickProxy, TasksProxy};
|
||||||
use yazi_scheduler::process::ProcessOpt;
|
use yazi_scheduler::process::ProcessOpt;
|
||||||
use yazi_shared::{data::Data, url::UrlCow};
|
use yazi_shared::{data::Data, url::{UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
@ -20,9 +20,9 @@ impl Actor for OpenDo {
|
||||||
let targets: Vec<_> = opt
|
let targets: Vec<_> = opt
|
||||||
.targets
|
.targets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| {
|
.map(|file| {
|
||||||
let m = cx.mgr.mimetype.get(&u).unwrap_or_default();
|
let mime = cx.mgr.mimetype.get(&file.url).unwrap_or_default();
|
||||||
(u, m)
|
(file, mime)
|
||||||
})
|
})
|
||||||
.filter(|(_, m)| !m.is_empty())
|
.filter(|(_, m)| !m.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -39,8 +39,10 @@ impl Actor for OpenDo {
|
||||||
}
|
}
|
||||||
|
|
||||||
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
||||||
let urls: Vec<_> =
|
let urls: Vec<_> = [UrlCow::default()]
|
||||||
[UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
|
.into_iter()
|
||||||
|
.chain(targets.into_iter().map(|(file, _)| file.url.into()))
|
||||||
|
.collect();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Some(choice) = pick.await {
|
if let Some(choice) = pick.await {
|
||||||
TasksProxy::open_shell_compat(ProcessOpt {
|
TasksProxy::open_shell_compat(ProcessOpt {
|
||||||
|
|
@ -59,11 +61,11 @@ impl Actor for OpenDo {
|
||||||
|
|
||||||
impl OpenDo {
|
impl OpenDo {
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
fn match_and_open(cx: &Ctx, cwd: UrlCow<'static>, targets: Vec<(UrlCow<'static>, &str)>) {
|
fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(yazi_fs::File, &str)>) {
|
||||||
let mut openers = HashMap::new();
|
let mut openers = HashMap::new();
|
||||||
for (url, mime) in targets {
|
for (file, mime) in targets {
|
||||||
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&url, mime)) {
|
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&file, mime)) {
|
||||||
openers.entry(opener).or_insert_with(|| vec![UrlCow::default()]).push(url);
|
openers.entry(opener).or_insert_with(|| vec![UrlCow::default()]).push(file.url.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (opener, args) in openers {
|
for (opener, args) in openers {
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ impl Actor for Seek {
|
||||||
succ!(cx.tab_mut().preview.reset());
|
succ!(cx.tab_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(previewer) = YAZI.plugin.previewer(hovered, mime) else {
|
let Some(previewer) = YAZI.plugin.previewers.matches(hovered, mime) else {
|
||||||
succ!(cx.tab_mut().preview.reset());
|
succ!(cx.tab_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let job = SeekJob { file: hovered.clone(), units: form.units };
|
let job = SeekJob { file: hovered.clone(), units: form.units };
|
||||||
let opt = PluginOpt::new_callback(&*previewer.run.name, move |_, plugin| {
|
let opt = PluginOpt::new_callback(previewer.name.clone(), move |_, plugin| {
|
||||||
plugin.call_method("seek", job)
|
plugin.call_method("seek", job)
|
||||||
});
|
});
|
||||||
act!(app:plugin, cx, opt)
|
act!(app:plugin, cx, opt)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl Actor for Shell {
|
||||||
fn act(cx: &mut Ctx, mut form: Self::Form) -> Result<Data> {
|
fn act(cx: &mut Ctx, mut form: Self::Form) -> Result<Data> {
|
||||||
act!(mgr:escape_visual, cx)?;
|
act!(mgr:escape_visual, cx)?;
|
||||||
|
|
||||||
let cwd = form.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
|
let cwd = form.cwd.take().unwrap_or_else(|| cx.cwd().clone());
|
||||||
let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
|
let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
|
||||||
|
|
||||||
let input = form.interactive.then(|| {
|
let input = form.interactive.then(|| {
|
||||||
|
|
@ -40,7 +40,7 @@ impl Actor for Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
TasksProxy::open_shell_compat(ProcessOpt {
|
TasksProxy::open_shell_compat(ProcessOpt {
|
||||||
cwd: cwd.into(),
|
cwd,
|
||||||
cmd: form.run.to_string().into(),
|
cmd: form.run.to_string().into(),
|
||||||
args: selected,
|
args: selected,
|
||||||
block: form.block,
|
block: form.block,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use yazi_macro::{render_and, succ};
|
use yazi_macro::{render_and, succ};
|
||||||
use yazi_parser::mgr::ToggleForm;
|
use yazi_parser::mgr::ToggleForm;
|
||||||
use yazi_scheduler::NotifyProxy;
|
use yazi_scheduler::NotifyProxy;
|
||||||
use yazi_shared::{data::Data, url::UrlCow};
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl Actor for Toggle {
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||||
let tab = cx.tab_mut();
|
let tab = cx.tab_mut();
|
||||||
let Some(url) = form.url.or(tab.current.hovered().map(|h| UrlCow::from(&h.url))) else {
|
let Some(url) = form.url.or_else(|| tab.hovered().map(|h| h.url.clone())) else {
|
||||||
succ!();
|
succ!();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ impl Actor for Upload {
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||||
for url in form.urls {
|
for url in form.urls {
|
||||||
cx.tasks.scheduler.file_upload(url.into_owned());
|
cx.tasks.scheduler.file_upload(url);
|
||||||
}
|
}
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ impl From<yazi_emulator::Brand> for Adapters {
|
||||||
B::Ghostty => vec![A::Kgp],
|
B::Ghostty => vec![A::Kgp],
|
||||||
B::Microsoft => vec![A::Sixel],
|
B::Microsoft => vec![A::Sixel],
|
||||||
B::Warp => vec![A::Iip, A::KgpOld],
|
B::Warp => vec![A::Iip, A::KgpOld],
|
||||||
B::Rio => vec![A::Iip, A::Sixel],
|
B::Rio => vec![A::Kgp],
|
||||||
B::BlackBox => vec![A::Sixel],
|
B::BlackBox => vec![A::Sixel],
|
||||||
B::VSCode => vec![A::Iip, A::Sixel],
|
B::VSCode => vec![A::Iip, A::Sixel],
|
||||||
B::Tabby => vec![A::Iip, A::Sixel],
|
B::Tabby => vec![A::Iip, A::Sixel],
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ pub struct Access(yazi_vfs::provider::Gate);
|
||||||
|
|
||||||
impl UserData for Access {
|
impl UserData for Access {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_function_mut("append", |_, (ud, append): (AnyUserData, bool)| {
|
methods.add_function("append", |_, (ud, append): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.append(append);
|
ud.borrow_mut::<Self>()?.0.append(append);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("create", |_, (ud, create): (AnyUserData, bool)| {
|
methods.add_function("create", |_, (ud, create): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.create(create);
|
ud.borrow_mut::<Self>()?.0.create(create);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("create_new", |_, (ud, create_new): (AnyUserData, bool)| {
|
methods.add_function("create_new", |_, (ud, create_new): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.create_new(create_new);
|
ud.borrow_mut::<Self>()?.0.create_new(create_new);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
@ -26,15 +26,15 @@ impl UserData for Access {
|
||||||
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
methods.add_function_mut("read", |_, (ud, read): (AnyUserData, bool)| {
|
methods.add_function("read", |_, (ud, read): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.read(read);
|
ud.borrow_mut::<Self>()?.0.read(read);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("truncate", |_, (ud, truncate): (AnyUserData, bool)| {
|
methods.add_function("truncate", |_, (ud, truncate): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.truncate(truncate);
|
ud.borrow_mut::<Self>()?.0.truncate(truncate);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("write", |_, (ud, write): (AnyUserData, bool)| {
|
methods.add_function("write", |_, (ud, write): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.0.write(write);
|
ud.borrow_mut::<Self>()?.0.write(write);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
32
yazi-binding/src/config/fetcher.rs
Normal file
32
yazi-binding/src/config/fetcher.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::{UserData, UserDataFields, Value};
|
||||||
|
|
||||||
|
use crate::{Id, cached_field};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Fetcher {
|
||||||
|
inner: Arc<yazi_config::plugin::Fetcher>,
|
||||||
|
|
||||||
|
v_name: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Fetcher {
|
||||||
|
type Target = yazi_config::plugin::Fetcher;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fetcher {
|
||||||
|
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Fetcher>>) -> Self {
|
||||||
|
Self { inner: inner.into(), v_name: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Fetcher {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
|
||||||
|
|
||||||
|
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
59
yazi-binding/src/config/fetchers.rs
Normal file
59
yazi-binding/src/config/fetchers.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
|
||||||
|
use yazi_config::YAZI;
|
||||||
|
|
||||||
|
use crate::{FileRef, Id, Iter, config::Fetcher};
|
||||||
|
|
||||||
|
pub struct Fetchers;
|
||||||
|
|
||||||
|
impl UserData for Fetchers {
|
||||||
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("match", |lua, _, matcher: Option<FetcherMatcher>| match matcher {
|
||||||
|
Some(matcher) => matcher.into_lua(lua),
|
||||||
|
None => FetcherMatcher::new(&YAZI.plugin.fetchers).into_lua(lua),
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.fetchers.load().len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
pub struct FetcherMatcher(yazi_config::plugin::FetcherMatcher<'static>);
|
||||||
|
|
||||||
|
impl FetcherMatcher {
|
||||||
|
fn new(inner: impl Into<yazi_config::plugin::FetcherMatcher<'static>>) -> Self {
|
||||||
|
Self(inner.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Table> for FetcherMatcher {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Table) -> Result<Self, Self::Error> {
|
||||||
|
let id: Id = value.raw_get("id").unwrap_or_default();
|
||||||
|
let file: Option<FileRef> = value.raw_get("file")?;
|
||||||
|
let mime: Option<String> = value.raw_get("mime")?;
|
||||||
|
|
||||||
|
Ok(Self(yazi_config::plugin::FetcherMatcher {
|
||||||
|
fetchers: YAZI.plugin.fetchers.load_full(),
|
||||||
|
id: id.0,
|
||||||
|
file: file.map(|f| f.inner.clone().into()),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for FetcherMatcher {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Table(t) => t.try_into(),
|
||||||
|
_ => return Err("expected a table of FetcherMatcher".into_lua_err()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for FetcherMatcher {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
Iter::new(self.0.into_iter().map(Fetcher::new), None).into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
yazi-binding/src/config/mod.rs
Normal file
1
yazi-binding/src/config/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
yazi_macro::mod_flat!(fetcher fetchers preloader preloaders previewer previewers spotter spotters);
|
||||||
32
yazi-binding/src/config/preloader.rs
Normal file
32
yazi-binding/src/config/preloader.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::{UserData, UserDataFields, Value};
|
||||||
|
|
||||||
|
use crate::{Id, cached_field};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Preloader {
|
||||||
|
inner: Arc<yazi_config::plugin::Preloader>,
|
||||||
|
|
||||||
|
v_name: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Preloader {
|
||||||
|
type Target = yazi_config::plugin::Preloader;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Preloader {
|
||||||
|
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Preloader>>) -> Self {
|
||||||
|
Self { inner: inner.into(), v_name: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Preloader {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
|
||||||
|
|
||||||
|
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
59
yazi-binding/src/config/preloaders.rs
Normal file
59
yazi-binding/src/config/preloaders.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
|
||||||
|
use yazi_config::YAZI;
|
||||||
|
|
||||||
|
use crate::{FileRef, Id, Iter, config::Preloader};
|
||||||
|
|
||||||
|
pub struct Preloaders;
|
||||||
|
|
||||||
|
impl UserData for Preloaders {
|
||||||
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("match", |lua, _, matcher: Option<PreloaderMatcher>| match matcher {
|
||||||
|
Some(matcher) => matcher.into_lua(lua),
|
||||||
|
None => PreloaderMatcher::new(&YAZI.plugin.preloaders).into_lua(lua),
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.preloaders.load().len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
pub struct PreloaderMatcher(yazi_config::plugin::PreloaderMatcher<'static>);
|
||||||
|
|
||||||
|
impl PreloaderMatcher {
|
||||||
|
fn new(inner: impl Into<yazi_config::plugin::PreloaderMatcher<'static>>) -> Self {
|
||||||
|
Self(inner.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Table> for PreloaderMatcher {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Table) -> Result<Self, Self::Error> {
|
||||||
|
let id: Id = value.raw_get("id").unwrap_or_default();
|
||||||
|
let file: Option<FileRef> = value.raw_get("file")?;
|
||||||
|
let mime: Option<String> = value.raw_get("mime")?;
|
||||||
|
|
||||||
|
Ok(Self(yazi_config::plugin::PreloaderMatcher {
|
||||||
|
preloaders: YAZI.plugin.preloaders.load_full(),
|
||||||
|
id: id.0,
|
||||||
|
file: file.map(|f| f.inner.clone().into()),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for PreloaderMatcher {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Table(t) => t.try_into(),
|
||||||
|
_ => return Err("expected a table of PreloaderMatcher".into_lua_err()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for PreloaderMatcher {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
Iter::new(self.0.into_iter().map(Preloader::new), None).into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
69
yazi-binding/src/config/previewer.rs
Normal file
69
yazi-binding/src/config/previewer.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::{AnyUserData, ExternalError, FromLua, Lua, UserData, UserDataFields, Value};
|
||||||
|
use yazi_config::plugin::previewer_id;
|
||||||
|
|
||||||
|
use crate::{Id, Selector, cached_field};
|
||||||
|
|
||||||
|
const EXPECTED: &str = "expected a table or Previewer";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Previewer {
|
||||||
|
inner: Arc<yazi_config::plugin::Previewer>,
|
||||||
|
|
||||||
|
v_name: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Previewer {
|
||||||
|
type Target = yazi_config::plugin::Previewer;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Previewer> for Arc<yazi_config::plugin::Previewer> {
|
||||||
|
fn from(value: Previewer) -> Self { value.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Previewer {
|
||||||
|
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Previewer>>) -> Self {
|
||||||
|
Self { inner: inner.into(), v_name: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<mlua::Table> for Previewer {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(t: mlua::Table) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self::new(yazi_config::plugin::Previewer {
|
||||||
|
id: previewer_id(),
|
||||||
|
selector: Selector::try_from(&t)?.into(),
|
||||||
|
run: t.raw_get::<mlua::String>("run")?.to_str()?.parse()?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<AnyUserData> for Previewer {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: AnyUserData) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self::new(value.borrow::<Self>()?.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for Previewer {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Table(tbl) => Self::try_from(tbl),
|
||||||
|
Value::UserData(ud) => Self::try_from(ud),
|
||||||
|
_ => Err(EXPECTED.into_lua_err())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Previewer {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
|
||||||
|
|
||||||
|
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
75
yazi-binding/src/config/previewers.rs
Normal file
75
yazi-binding/src/config/previewers.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
use mlua::{ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
|
||||||
|
use yazi_config::YAZI;
|
||||||
|
|
||||||
|
use crate::{FileRef, Id, Iter, config::Previewer};
|
||||||
|
|
||||||
|
pub struct Previewers;
|
||||||
|
|
||||||
|
impl UserData for Previewers {
|
||||||
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("match", |lua, _, matcher: Option<PreviewerMatcher>| match matcher {
|
||||||
|
Some(matcher) => matcher.into_lua(lua),
|
||||||
|
None => PreviewerMatcher::new(&YAZI.plugin.previewers).into_lua(lua),
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("insert", |_, _, (index, previewer): (isize, Previewer)| {
|
||||||
|
let index = match index {
|
||||||
|
1.. => index - 1,
|
||||||
|
0 => return Err("index must be 1-based or negative".into_lua_err()),
|
||||||
|
_ => index,
|
||||||
|
};
|
||||||
|
|
||||||
|
YAZI.plugin.previewers.insert(index, previewer.clone().into()).into_lua_err()?;
|
||||||
|
Ok(previewer)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("remove", |_, _, matcher: PreviewerMatcher| {
|
||||||
|
YAZI.plugin.previewers.remove(matcher.0);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.previewers.load().len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
pub struct PreviewerMatcher(yazi_config::plugin::PreviewerMatcher<'static>);
|
||||||
|
|
||||||
|
impl PreviewerMatcher {
|
||||||
|
fn new(inner: impl Into<yazi_config::plugin::PreviewerMatcher<'static>>) -> Self {
|
||||||
|
Self(inner.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Table> for PreviewerMatcher {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Table) -> Result<Self, Self::Error> {
|
||||||
|
let id: Id = value.raw_get("id").unwrap_or_default();
|
||||||
|
let file: Option<FileRef> = value.raw_get("file")?;
|
||||||
|
let mime: Option<String> = value.raw_get("mime")?;
|
||||||
|
|
||||||
|
Ok(Self(yazi_config::plugin::PreviewerMatcher {
|
||||||
|
previewers: YAZI.plugin.previewers.load_full(),
|
||||||
|
id: id.0,
|
||||||
|
file: file.map(|f| f.inner.clone().into()),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for PreviewerMatcher {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Table(t) => t.try_into(),
|
||||||
|
_ => return Err("expected a table of PreviewerMatcher".into_lua_err()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for PreviewerMatcher {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
Iter::new(self.0.into_iter().map(Previewer::new), None).into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
32
yazi-binding/src/config/spotter.rs
Normal file
32
yazi-binding/src/config/spotter.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::{UserData, UserDataFields, Value};
|
||||||
|
|
||||||
|
use crate::{Id, cached_field};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Spotter {
|
||||||
|
inner: Arc<yazi_config::plugin::Spotter>,
|
||||||
|
|
||||||
|
v_name: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Spotter {
|
||||||
|
type Target = yazi_config::plugin::Spotter;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spotter {
|
||||||
|
pub fn new(inner: impl Into<Arc<yazi_config::plugin::Spotter>>) -> Self {
|
||||||
|
Self { inner: inner.into(), v_name: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Spotter {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("id", |_, me| Ok(Id(me.id)));
|
||||||
|
|
||||||
|
cached_field!(fields, name, |lua, me| lua.create_string(&*me.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
59
yazi-binding/src/config/spotters.rs
Normal file
59
yazi-binding/src/config/spotters.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
|
||||||
|
use yazi_config::YAZI;
|
||||||
|
|
||||||
|
use crate::{FileRef, Id, Iter, config::Spotter};
|
||||||
|
|
||||||
|
pub struct Spotters;
|
||||||
|
|
||||||
|
impl UserData for Spotters {
|
||||||
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("match", |lua, _, matcher: Option<SpotterMatcher>| match matcher {
|
||||||
|
Some(matcher) => matcher.into_lua(lua),
|
||||||
|
None => SpotterMatcher::new(&YAZI.plugin.spotters).into_lua(lua),
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(MetaMethod::Len, |_, _, ()| Ok(YAZI.plugin.spotters.load().len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
pub struct SpotterMatcher(yazi_config::plugin::SpotterMatcher<'static>);
|
||||||
|
|
||||||
|
impl SpotterMatcher {
|
||||||
|
fn new(inner: impl Into<yazi_config::plugin::SpotterMatcher<'static>>) -> Self {
|
||||||
|
Self(inner.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Table> for SpotterMatcher {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Table) -> Result<Self, Self::Error> {
|
||||||
|
let id: Id = value.raw_get("id").unwrap_or_default();
|
||||||
|
let file: Option<FileRef> = value.raw_get("file")?;
|
||||||
|
let mime: Option<String> = value.raw_get("mime")?;
|
||||||
|
|
||||||
|
Ok(Self(yazi_config::plugin::SpotterMatcher {
|
||||||
|
spotters: YAZI.plugin.spotters.load_full(),
|
||||||
|
id: id.0,
|
||||||
|
file: file.map(|f| f.inner.clone().into()),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for SpotterMatcher {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
Value::Table(t) => t.try_into(),
|
||||||
|
_ => return Err("expected a table of SpotterMatcher".into_lua_err()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for SpotterMatcher {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
Iter::new(self.0.into_iter().map(Spotter::new), None).into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -81,11 +81,11 @@ impl UserData for Bar {
|
||||||
crate::impl_area_method!(methods);
|
crate::impl_area_method!(methods);
|
||||||
crate::impl_style_method!(methods, style);
|
crate::impl_style_method!(methods, style);
|
||||||
|
|
||||||
methods.add_function_mut("edge", |_, (ud, edge): (AnyUserData, Edge)| {
|
methods.add_function("edge", |_, (ud, edge): (AnyUserData, Edge)| {
|
||||||
ud.borrow_mut::<Self>()?.edge = edge;
|
ud.borrow_mut::<Self>()?.edge = edge;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("symbol", |_, (ud, symbol): (AnyUserData, String)| {
|
methods.add_function("symbol", |_, (ud, symbol): (AnyUserData, String)| {
|
||||||
ud.borrow_mut::<Self>()?.symbol = symbol;
|
ud.borrow_mut::<Self>()?.symbol = symbol;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ impl UserData for Border {
|
||||||
crate::impl_area_method!(methods);
|
crate::impl_area_method!(methods);
|
||||||
crate::impl_style_method!(methods, style);
|
crate::impl_style_method!(methods, style);
|
||||||
|
|
||||||
methods.add_function_mut("type", |_, (ud, value): (AnyUserData, u8)| {
|
methods.add_function("type", |_, (ud, value): (AnyUserData, u8)| {
|
||||||
ud.borrow_mut::<Self>()?.r#type = match value {
|
ud.borrow_mut::<Self>()?.r#type = match value {
|
||||||
ROUNDED => ratatui::widgets::BorderType::Rounded,
|
ROUNDED => ratatui::widgets::BorderType::Rounded,
|
||||||
DOUBLE => ratatui::widgets::BorderType::Double,
|
DOUBLE => ratatui::widgets::BorderType::Double,
|
||||||
|
|
@ -90,20 +90,17 @@ impl UserData for Border {
|
||||||
};
|
};
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut(
|
methods.add_function("title", |_, (ud, line, position): (AnyUserData, Line, Option<u8>)| {
|
||||||
"title",
|
let position = if position == Some(Borders::BOTTOM.bits()) {
|
||||||
|_, (ud, line, position): (AnyUserData, Line, Option<u8>)| {
|
ratatui::widgets::TitlePosition::Bottom
|
||||||
let position = if position == Some(Borders::BOTTOM.bits()) {
|
} else {
|
||||||
ratatui::widgets::TitlePosition::Bottom
|
ratatui::widgets::TitlePosition::Top
|
||||||
} else {
|
};
|
||||||
ratatui::widgets::TitlePosition::Top
|
|
||||||
};
|
|
||||||
|
|
||||||
ud.borrow_mut::<Self>()?.titles.push((position, line.inner));
|
ud.borrow_mut::<Self>()?.titles.push((position, line.inner));
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
},
|
});
|
||||||
);
|
methods.add_function("edge", |_, (ud, edge): (AnyUserData, Edge)| {
|
||||||
methods.add_function_mut("edge", |_, (ud, edge): (AnyUserData, Edge)| {
|
|
||||||
ud.borrow_mut::<Self>()?.edge = edge;
|
ud.borrow_mut::<Self>()?.edge = edge;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ impl UserData for Gauge {
|
||||||
crate::impl_area_method!(methods);
|
crate::impl_area_method!(methods);
|
||||||
crate::impl_style_method!(methods, style);
|
crate::impl_style_method!(methods, style);
|
||||||
|
|
||||||
methods.add_function_mut("percent", |_, (ud, percent): (AnyUserData, u8)| {
|
methods.add_function("percent", |_, (ud, percent): (AnyUserData, u8)| {
|
||||||
if percent > 100 {
|
if percent > 100 {
|
||||||
return Err("percent must be between 0 and 100".into_lua_err());
|
return Err("percent must be between 0 and 100".into_lua_err());
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +66,7 @@ impl UserData for Gauge {
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function_mut("ratio", |_, (ud, ratio): (AnyUserData, f64)| {
|
methods.add_function("ratio", |_, (ud, ratio): (AnyUserData, f64)| {
|
||||||
if !(0.0..1.0).contains(&ratio) {
|
if !(0.0..1.0).contains(&ratio) {
|
||||||
return Err("ratio must be between 0 and 1".into_lua_err());
|
return Err("ratio must be between 0 and 1".into_lua_err());
|
||||||
}
|
}
|
||||||
|
|
@ -75,12 +75,12 @@ impl UserData for Gauge {
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function_mut("label", |_, (ud, label): (AnyUserData, Span)| {
|
methods.add_function("label", |_, (ud, label): (AnyUserData, Span)| {
|
||||||
ud.borrow_mut::<Self>()?.label = Some(label.0);
|
ud.borrow_mut::<Self>()?.label = Some(label.0);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function_mut("gauge_style", |_, (ud, style): (AnyUserData, Style)| {
|
methods.add_function("gauge_style", |_, (ud, style): (AnyUserData, Style)| {
|
||||||
ud.borrow_mut::<Self>()?.gauge_style = style.0;
|
ud.borrow_mut::<Self>()?.gauge_style = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,15 @@ impl Layout {
|
||||||
|
|
||||||
impl UserData for Layout {
|
impl UserData for Layout {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_function_mut("direction", |_, (ud, value): (AnyUserData, bool)| {
|
methods.add_function("direction", |_, (ud, value): (AnyUserData, bool)| {
|
||||||
ud.borrow_mut::<Self>()?.direction = value;
|
ud.borrow_mut::<Self>()?.direction = value;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("margin", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("margin", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.margin = Some(ratatui::layout::Margin::new(value, value));
|
ud.borrow_mut::<Self>()?.margin = Some(ratatui::layout::Margin::new(value, value));
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("margin_h", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("margin_h", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
{
|
{
|
||||||
let mut me = ud.borrow_mut::<Self>()?;
|
let mut me = ud.borrow_mut::<Self>()?;
|
||||||
if let Some(margin) = &mut me.margin {
|
if let Some(margin) = &mut me.margin {
|
||||||
|
|
@ -44,7 +44,7 @@ impl UserData for Layout {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("margin_v", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("margin_v", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
{
|
{
|
||||||
let mut me = ud.borrow_mut::<Self>()?;
|
let mut me = ud.borrow_mut::<Self>()?;
|
||||||
if let Some(margin) = &mut me.margin {
|
if let Some(margin) = &mut me.margin {
|
||||||
|
|
@ -55,7 +55,7 @@ impl UserData for Layout {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("constraints", |_, (ud, value): (AnyUserData, Vec<Constraint>)| {
|
methods.add_function("constraints", |_, (ud, value): (AnyUserData, Vec<Constraint>)| {
|
||||||
ud.borrow_mut::<Self>()?.constraints = value.into_iter().map(Into::into).collect();
|
ud.borrow_mut::<Self>()?.constraints = value.into_iter().map(Into::into).collect();
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -133,14 +133,14 @@ impl UserData for Line {
|
||||||
crate::impl_style_shorthands!(methods, style);
|
crate::impl_style_shorthands!(methods, style);
|
||||||
|
|
||||||
methods.add_method("width", |_, me, ()| Ok(me.width()));
|
methods.add_method("width", |_, me, ()| Ok(me.width()));
|
||||||
methods.add_function_mut("align", |_, (ud, align): (AnyUserData, Align)| {
|
methods.add_function("align", |_, (ud, align): (AnyUserData, Align)| {
|
||||||
ud.borrow_mut::<Self>()?.alignment = Some(align.0);
|
ud.borrow_mut::<Self>()?.alignment = Some(align.0);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_method("visible", |_, me, ()| {
|
methods.add_method("visible", |_, me, ()| {
|
||||||
Ok(me.iter().flat_map(|s| s.content.chars()).any(|c| c.width().unwrap_or(0) > 0))
|
Ok(me.iter().flat_map(|s| s.content.chars()).any(|c| c.width().unwrap_or(0) > 0))
|
||||||
});
|
});
|
||||||
methods.add_function_mut("truncate", |lua, (ud, t): (AnyUserData, Table)| {
|
methods.add_function("truncate", |lua, (ud, t): (AnyUserData, Table)| {
|
||||||
let mut me = ud.borrow_mut::<Self>()?;
|
let mut me = ud.borrow_mut::<Self>()?;
|
||||||
|
|
||||||
let max = t.raw_get("max")?;
|
let max = t.raw_get("max")?;
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ impl UserData for Pos {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_function_mut("pad", |_, (ud, pad): (AnyUserData, Pad)| {
|
methods.add_function("pad", |_, (ud, pad): (AnyUserData, Pad)| {
|
||||||
ud.borrow_mut::<Self>()?.pad = pad;
|
ud.borrow_mut::<Self>()?.pad = pad;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -55,15 +55,15 @@ impl UserData for Row {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
crate::impl_style_method!(methods, style);
|
crate::impl_style_method!(methods, style);
|
||||||
|
|
||||||
methods.add_function_mut("height", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("height", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.height = value;
|
ud.borrow_mut::<Self>()?.height = value;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("margin_t", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("margin_t", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.top_margin = value;
|
ud.borrow_mut::<Self>()?.top_margin = value;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("margin_b", |_, (ud, value): (AnyUserData, u16)| {
|
methods.add_function("margin_b", |_, (ud, value): (AnyUserData, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.bottom_margin = value;
|
ud.borrow_mut::<Self>()?.bottom_margin = value;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ impl UserData for Span {
|
||||||
methods.add_method("visible", |_, Self(me), ()| {
|
methods.add_method("visible", |_, Self(me), ()| {
|
||||||
Ok(me.content.chars().any(|c| c.width().unwrap_or(0) > 0))
|
Ok(me.content.chars().any(|c| c.width().unwrap_or(0) > 0))
|
||||||
});
|
});
|
||||||
methods.add_function_mut("truncate", |_, (ud, t): (AnyUserData, Table)| {
|
methods.add_function("truncate", |_, (ud, t): (AnyUserData, Table)| {
|
||||||
ud.borrow_mut::<Self>()?.truncate(t.raw_get("max")?);
|
ud.borrow_mut::<Self>()?.truncate(t.raw_get("max")?);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -108,45 +108,45 @@ impl UserData for Table {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
crate::impl_area_method!(methods);
|
crate::impl_area_method!(methods);
|
||||||
|
|
||||||
methods.add_function_mut("header", |_, (ud, header): (AnyUserData, Row)| {
|
methods.add_function("header", |_, (ud, header): (AnyUserData, Row)| {
|
||||||
ud.borrow_mut::<Self>()?.header = Some(header.into());
|
ud.borrow_mut::<Self>()?.header = Some(header.into());
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("footer", |_, (ud, footer): (AnyUserData, Row)| {
|
methods.add_function("footer", |_, (ud, footer): (AnyUserData, Row)| {
|
||||||
ud.borrow_mut::<Self>()?.footer = Some(footer.into());
|
ud.borrow_mut::<Self>()?.footer = Some(footer.into());
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("widths", |_, (ud, widths): (AnyUserData, Vec<Constraint>)| {
|
methods.add_function("widths", |_, (ud, widths): (AnyUserData, Vec<Constraint>)| {
|
||||||
ud.borrow_mut::<Self>()?.widths = widths.into_iter().map(Into::into).collect();
|
ud.borrow_mut::<Self>()?.widths = widths.into_iter().map(Into::into).collect();
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("spacing", |_, (ud, spacing): (AnyUserData, u16)| {
|
methods.add_function("spacing", |_, (ud, spacing): (AnyUserData, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.column_spacing = spacing;
|
ud.borrow_mut::<Self>()?.column_spacing = spacing;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function_mut("row", |_, (ud, idx): (AnyUserData, Option<usize>)| {
|
methods.add_function("row", |_, (ud, idx): (AnyUserData, Option<usize>)| {
|
||||||
ud.borrow_mut::<Self>()?.state.select(idx);
|
ud.borrow_mut::<Self>()?.state.select(idx);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("col", |_, (ud, idx): (AnyUserData, Option<usize>)| {
|
methods.add_function("col", |_, (ud, idx): (AnyUserData, Option<usize>)| {
|
||||||
ud.borrow_mut::<Self>()?.state.select_column(idx);
|
ud.borrow_mut::<Self>()?.state.select_column(idx);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function_mut("style", |_, (ud, style): (AnyUserData, Style)| {
|
methods.add_function("style", |_, (ud, style): (AnyUserData, Style)| {
|
||||||
ud.borrow_mut::<Self>()?.style = style.0;
|
ud.borrow_mut::<Self>()?.style = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("row_style", |_, (ud, style): (AnyUserData, Style)| {
|
methods.add_function("row_style", |_, (ud, style): (AnyUserData, Style)| {
|
||||||
ud.borrow_mut::<Self>()?.row_highlight_style = style.0;
|
ud.borrow_mut::<Self>()?.row_highlight_style = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("col_style", |_, (ud, style): (AnyUserData, Style)| {
|
methods.add_function("col_style", |_, (ud, style): (AnyUserData, Style)| {
|
||||||
ud.borrow_mut::<Self>()?.column_highlight_style = style.0;
|
ud.borrow_mut::<Self>()?.column_highlight_style = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("cell_style", |_, (ud, style): (AnyUserData, Style)| {
|
methods.add_function("cell_style", |_, (ud, style): (AnyUserData, Style)| {
|
||||||
ud.borrow_mut::<Self>()?.cell_highlight_style = style.0;
|
ud.borrow_mut::<Self>()?.cell_highlight_style = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -124,15 +124,15 @@ impl UserData for Text {
|
||||||
crate::impl_style_method!(methods, inner.style);
|
crate::impl_style_method!(methods, inner.style);
|
||||||
crate::impl_style_shorthands!(methods, inner.style);
|
crate::impl_style_shorthands!(methods, inner.style);
|
||||||
|
|
||||||
methods.add_function_mut("align", |_, (ud, align): (AnyUserData, Align)| {
|
methods.add_function("align", |_, (ud, align): (AnyUserData, Align)| {
|
||||||
ud.borrow_mut::<Self>()?.inner.alignment = Some(align.0);
|
ud.borrow_mut::<Self>()?.inner.alignment = Some(align.0);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("wrap", |_, (ud, wrap): (AnyUserData, Wrap)| {
|
methods.add_function("wrap", |_, (ud, wrap): (AnyUserData, Wrap)| {
|
||||||
ud.borrow_mut::<Self>()?.wrap = wrap;
|
ud.borrow_mut::<Self>()?.wrap = wrap;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("scroll", |_, (ud, x, y): (AnyUserData, u16, u16)| {
|
methods.add_function("scroll", |_, (ud, x, y): (AnyUserData, u16, u16)| {
|
||||||
ud.borrow_mut::<Self>()?.scroll = ratatui::layout::Position { x, y };
|
ud.borrow_mut::<Self>()?.scroll = ratatui::layout::Position { x, y };
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const EXPECTED: &str = "expected a table, File, or fs::File";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
inner: yazi_fs::File,
|
pub(crate) inner: yazi_fs::File,
|
||||||
|
|
||||||
v_cha: Option<Value>,
|
v_cha: Option<Value>,
|
||||||
v_url: Option<Value>,
|
v_url: Option<Value>,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use mlua::{ExternalError, ExternalResult, FromLua, Lua, UserData, Value};
|
use mlua::{ExternalError, ExternalResult, FromLua, Lua, UserData, Value};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct Id(pub yazi_shared::Id);
|
pub struct Id(pub yazi_shared::Id);
|
||||||
|
|
||||||
impl Deref for Id {
|
impl Deref for Id {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
yazi_macro::mod_pub!(elements process);
|
yazi_macro::mod_pub!(config elements process);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(access calculator cha chan chord_cow composer error fd file handle icon id image input iter layer mouse path permit range runtime scheme stage style url utils);
|
yazi_macro::mod_flat!(access calculator cha chan chord_cow composer error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style url utils);
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ macro_rules! cached_field_mut {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_area_method {
|
macro_rules! impl_area_method {
|
||||||
($methods:ident) => {
|
($methods:ident) => {
|
||||||
$methods.add_function_mut(
|
$methods.add_function(
|
||||||
"area",
|
"area",
|
||||||
|lua, (ud, area): (mlua::AnyUserData, Option<mlua::AnyUserData>)| {
|
|lua, (ud, area): (mlua::AnyUserData, Option<mlua::AnyUserData>)| {
|
||||||
use mlua::IntoLua;
|
use mlua::IntoLua;
|
||||||
|
|
@ -106,7 +106,7 @@ macro_rules! impl_area_method {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_style_method {
|
macro_rules! impl_style_method {
|
||||||
($methods:ident, $($field:tt).+) => {
|
($methods:ident, $($field:tt).+) => {
|
||||||
$methods.add_function_mut("style", |_, (ud, style): (mlua::AnyUserData, $crate::Style)| {
|
$methods.add_function("style", |_, (ud, style): (mlua::AnyUserData, $crate::Style)| {
|
||||||
ud.borrow_mut::<Self>()?.$($field).+ = style.0;
|
ud.borrow_mut::<Self>()?.$($field).+ = style.0;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
@ -116,7 +116,7 @@ macro_rules! impl_style_method {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_style_shorthands {
|
macro_rules! impl_style_shorthands {
|
||||||
($methods:ident, $($field:tt).+) => {
|
($methods:ident, $($field:tt).+) => {
|
||||||
$methods.add_function_mut("fg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| {
|
$methods.add_function("fg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| {
|
||||||
use $crate::elements::Color;
|
use $crate::elements::Color;
|
||||||
use mlua::FromLua;
|
use mlua::FromLua;
|
||||||
use ratatui::style::Modifier;
|
use ratatui::style::Modifier;
|
||||||
|
|
@ -135,7 +135,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("bg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| {
|
$methods.add_function("bg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| {
|
||||||
use $crate::elements::Color;
|
use $crate::elements::Color;
|
||||||
use mlua::FromLua;
|
use mlua::FromLua;
|
||||||
use ratatui::style::Modifier;
|
use ratatui::style::Modifier;
|
||||||
|
|
@ -154,7 +154,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("bold", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("bold", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::BOLD);
|
*me = me.remove_modifier(ratatui::style::Modifier::BOLD);
|
||||||
|
|
@ -163,7 +163,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("dim", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("dim", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::DIM);
|
*me = me.remove_modifier(ratatui::style::Modifier::DIM);
|
||||||
|
|
@ -172,7 +172,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("italic", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("italic", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::ITALIC);
|
*me = me.remove_modifier(ratatui::style::Modifier::ITALIC);
|
||||||
|
|
@ -181,7 +181,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("underline", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("underline", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::UNDERLINED);
|
*me = me.remove_modifier(ratatui::style::Modifier::UNDERLINED);
|
||||||
|
|
@ -190,7 +190,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("blink", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("blink", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::SLOW_BLINK);
|
*me = me.remove_modifier(ratatui::style::Modifier::SLOW_BLINK);
|
||||||
|
|
@ -199,7 +199,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("blink_rapid", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("blink_rapid", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::RAPID_BLINK);
|
*me = me.remove_modifier(ratatui::style::Modifier::RAPID_BLINK);
|
||||||
|
|
@ -208,7 +208,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("reverse", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("reverse", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::REVERSED);
|
*me = me.remove_modifier(ratatui::style::Modifier::REVERSED);
|
||||||
|
|
@ -217,7 +217,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("hidden", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("hidden", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::HIDDEN);
|
*me = me.remove_modifier(ratatui::style::Modifier::HIDDEN);
|
||||||
|
|
@ -226,7 +226,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("crossed", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
$methods.add_function("crossed", |_, (ud, remove): (mlua::AnyUserData, bool)| {
|
||||||
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
let me = &mut ud.borrow_mut::<Self>()?.$($field).+;
|
||||||
if remove {
|
if remove {
|
||||||
*me = me.remove_modifier(ratatui::style::Modifier::CROSSED_OUT);
|
*me = me.remove_modifier(ratatui::style::Modifier::CROSSED_OUT);
|
||||||
|
|
@ -235,7 +235,7 @@ macro_rules! impl_style_shorthands {
|
||||||
}
|
}
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
$methods.add_function_mut("reset", |_, ud: mlua::AnyUserData| {
|
$methods.add_function("reset", |_, ud: mlua::AnyUserData| {
|
||||||
ud.borrow_mut::<Self>()?.$($field).+ = ratatui::style::Style::reset();
|
ud.borrow_mut::<Self>()?.$($field).+ = ratatui::style::Style::reset();
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,9 @@ use std::{ops::DerefMut, process::ExitStatus, time::Duration};
|
||||||
use futures::future::try_join3;
|
use futures::future::try_join3;
|
||||||
use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, UserDataMethods, Value};
|
use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, UserDataMethods, Value};
|
||||||
use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
|
use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
|
||||||
use crate::Error;
|
|
||||||
|
|
||||||
use super::Status;
|
use super::Status;
|
||||||
use crate::process::Output;
|
use crate::{Error, process::Output};
|
||||||
|
|
||||||
pub struct Child {
|
pub struct Child {
|
||||||
inner: tokio::process::Child,
|
inner: tokio::process::Child,
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ impl UserData for Command {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
methods.add_function_mut("arg", |lua, (ud, arg): (AnyUserData, Value)| {
|
methods.add_function("arg", |lua, (ud, arg): (AnyUserData, Value)| {
|
||||||
let mut me = ud.borrow_mut::<Self>()?;
|
let mut me = ud.borrow_mut::<Self>()?;
|
||||||
match arg {
|
match arg {
|
||||||
Value::Nil => return lua.create_sequence_from(me.inner.as_std().get_args())?.into_lua(lua),
|
Value::Nil => return lua.create_sequence_from(me.inner.as_std().get_args())?.into_lua(lua),
|
||||||
|
|
@ -154,11 +154,11 @@ impl UserData for Command {
|
||||||
}
|
}
|
||||||
ud.into_lua(lua)
|
ud.into_lua(lua)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| {
|
methods.add_function("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| {
|
||||||
ud.borrow_mut::<Self>()?.inner.current_dir(dir.to_str()?.as_ref());
|
ud.borrow_mut::<Self>()?.inner.current_dir(dir.to_str()?.as_ref());
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut(
|
methods.add_function(
|
||||||
"env",
|
"env",
|
||||||
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
|
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
|
||||||
ud.borrow_mut::<Self>()?
|
ud.borrow_mut::<Self>()?
|
||||||
|
|
@ -167,19 +167,19 @@ impl UserData for Command {
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
methods.add_function_mut("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
|
methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
|
||||||
ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
|
ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
|
methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
|
||||||
ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
|
ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("stderr", |_, (ud, stdio): (AnyUserData, Value)| {
|
methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, Value)| {
|
||||||
ud.borrow_mut::<Self>()?.inner.stderr(make_stdio(stdio)?);
|
ud.borrow_mut::<Self>()?.inner.stderr(make_stdio(stdio)?);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
methods.add_function_mut("memory", |_, (ud, max): (AnyUserData, usize)| {
|
methods.add_function("memory", |_, (ud, max): (AnyUserData, usize)| {
|
||||||
ud.borrow_mut::<Self>()?.memory = Some(max);
|
ud.borrow_mut::<Self>()?.memory = Some(max);
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use mlua::{UserData, Value};
|
use mlua::{UserData, Value};
|
||||||
use crate::{cached_field, cached_field_mut};
|
|
||||||
|
|
||||||
use super::Status;
|
use super::Status;
|
||||||
|
use crate::{cached_field, cached_field_mut};
|
||||||
|
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
inner: std::process::Output,
|
inner: std::process::Output,
|
||||||
|
|
|
||||||
43
yazi-binding/src/selector.rs
Normal file
43
yazi-binding/src/selector.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use mlua::{ExternalError, FromLua, Lua, Table, UserData, Value};
|
||||||
|
|
||||||
|
pub struct Selector(yazi_config::Selector);
|
||||||
|
|
||||||
|
impl Deref for Selector {
|
||||||
|
type Target = yazi_config::Selector;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Selector> for yazi_config::Selector {
|
||||||
|
fn from(value: Selector) -> Self { value.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Table> for Selector {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(t: &Table) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self(yazi_config::Selector::new(
|
||||||
|
t.raw_get::<Option<mlua::String>>("url")?.map(|s| s.to_str()?.parse()).transpose()?,
|
||||||
|
t.raw_get::<Option<mlua::String>>("mime")?.map(|s| s.to_str()?.parse()).transpose()?,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Table> for Selector {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Table) -> Result<Self, Self::Error> { Self::try_from(&value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for Selector {
|
||||||
|
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
Ok(match value {
|
||||||
|
Value::Table(t) => t.try_into()?,
|
||||||
|
_ => Err("expected a table of Selector".into_lua_err())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Selector {}
|
||||||
|
|
@ -2,6 +2,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value};
|
||||||
use ratatui::style::Modifier;
|
use ratatui::style::Modifier;
|
||||||
|
use yazi_shared::SyncCell;
|
||||||
|
|
||||||
use crate::{SER_OPT, elements::Color};
|
use crate::{SER_OPT, elements::Color};
|
||||||
|
|
||||||
|
|
@ -22,6 +23,10 @@ impl From<Style> for ratatui::style::Style {
|
||||||
fn from(value: Style) -> ratatui::style::Style { value.0 }
|
fn from(value: Style) -> ratatui::style::Style { value.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&SyncCell<yazi_config::Style>> for Style {
|
||||||
|
fn from(value: &SyncCell<yazi_config::Style>) -> Self { value.get().into() }
|
||||||
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
|
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
|
||||||
let new = lua.create_function(|_, (_, style): (Table, Self)| Ok(style))?;
|
let new = lua.create_function(|_, (_, style): (Table, Self)| Ok(style))?;
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ impl UserData for Url {
|
||||||
methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base));
|
methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base));
|
||||||
methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base));
|
methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base));
|
||||||
|
|
||||||
methods.add_function_mut("into_search", |_, (ud, domain): (AnyUserData, mlua::String)| {
|
methods.add_function("into_search", |_, (ud, domain): (AnyUserData, mlua::String)| {
|
||||||
let url = ud.take::<Self>()?.inner.into_search(domain.to_str()?).into_lua_err()?;
|
let url = ud.take::<Self>()?.inner.into_search(domain.to_str()?).into_lua_err()?;
|
||||||
Ok(Self::new(url))
|
Ok(Self::new(url))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
clap_complete = "4.6.1"
|
clap_complete = "4.6.2"
|
||||||
clap_complete_fig = "4.5.2"
|
clap_complete_fig = "4.5.2"
|
||||||
clap_complete_nushell = "4.6.0"
|
clap_complete_nushell = "4.6.0"
|
||||||
vergen-gitcl = { version = "9.1.0", features = [ "build", "rustc" ] }
|
vergen-gitcl = { version = "9.1.0", features = [ "build", "rustc" ] }
|
||||||
|
|
|
||||||
|
|
@ -71,17 +71,17 @@ impl Actions {
|
||||||
writeln!(
|
writeln!(
|
||||||
s,
|
s,
|
||||||
" default : {:?}",
|
" default : {:?}",
|
||||||
YAZI.opener.first(YAZI.open.all(Path::new("f75a.txt"), "text/plain"))
|
YAZI.opener.first(YAZI.open.all_dummy(Path::new("f75a.txt"), "text/plain"))
|
||||||
)?;
|
)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
s,
|
s,
|
||||||
" block-create: {:?}",
|
" block-create: {:?}",
|
||||||
YAZI.opener.block(YAZI.open.all(Path::new("bulk-create.txt"), "text/plain"))
|
YAZI.opener.block(YAZI.open.all_dummy(Path::new("bulk-create.txt"), "text/plain"))
|
||||||
)?;
|
)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
s,
|
s,
|
||||||
" block-rename: {:?}",
|
" block-rename: {:?}",
|
||||||
YAZI.opener.block(YAZI.open.all(Path::new("bulk-rename.txt"), "text/plain"))
|
YAZI.opener.block(YAZI.open.all_dummy(Path::new("bulk-rename.txt"), "text/plain"))
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
writeln!(s, "\nMultiplexers")?;
|
writeln!(s, "\nMultiplexers")?;
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||||
# External build dependencies
|
# External build dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
clap_complete = "4.6.1"
|
clap_complete = "4.6.2"
|
||||||
clap_complete_fig = "4.5.2"
|
clap_complete_fig = "4.5.2"
|
||||||
clap_complete_nushell = "4.6.0"
|
clap_complete_nushell = "4.6.0"
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::{format_ident, quote};
|
||||||
use syn::{Data, DeriveInput, Fields, parse_macro_input};
|
use syn::{Data, DeriveInput, Fields, parse_macro_input};
|
||||||
|
|
||||||
#[proc_macro_derive(DeserializeOver)]
|
#[proc_macro_derive(DeserializeOver)]
|
||||||
pub fn deserialize_over(input: TokenStream) -> TokenStream {
|
pub fn deserialize_over(input: TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, .. } = parse_macro_input!(input as DeriveInput);
|
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #ident {
|
impl #impl_generics yazi_shim::toml::DeserializeOverHook for #ident #ty_generics #where_clause {}
|
||||||
pub(crate) fn deserialize_over(self, input: &str) -> Result<Self, toml::de::Error> {
|
|
||||||
crate::error_with_input(self.deserialize_over_with(toml::de::DeTable::parse(input)?), input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(DeserializeOver1)]
|
#[proc_macro_derive(DeserializeOver1)]
|
||||||
pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);
|
let DeriveInput { ident, generics, data, .. } = parse_macro_input!(input as DeriveInput);
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
let assignments = match data {
|
let assignments = match data {
|
||||||
Data::Struct(struct_) => match struct_.fields {
|
Data::Struct(struct_) => match struct_.fields {
|
||||||
|
|
@ -32,7 +30,7 @@ pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||||
assignments.push(quote! {
|
assignments.push(quote! {
|
||||||
if let Some(value) = table.remove(#field_name) {
|
if let Some(value) = table.remove(#field_name) {
|
||||||
if !matches!(value.get_ref(), toml::de::DeValue::Table(_)) {
|
if !matches!(value.get_ref(), toml::de::DeValue::Table(_)) {
|
||||||
_ = toml::Table::deserialize(value.into_deserializer())?;
|
_ = deserialize_spanned::<toml::Table>(value)?;
|
||||||
return Err(serde::de::Error::custom(format!("expected top-level `{}` to be a TOML table", #field_name)));
|
return Err(serde::de::Error::custom(format!("expected top-level `{}` to be a TOML table", #field_name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,6 +39,7 @@ pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||||
self.#field_ident = self.#field_ident.deserialize_over_with(toml::Spanned::new(span, table))?;
|
self.#field_ident = self.#field_ident.deserialize_over_with(toml::Spanned::new(span, table))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.#field_ident = self.#field_ident.deserialize_over_hook()?;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,14 +51,14 @@ pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #ident {
|
impl #impl_generics yazi_shim::toml::DeserializeOverWith for #ident #ty_generics #where_clause {
|
||||||
pub(crate) fn deserialize_over_with<'de>(mut self, table: toml::Spanned<toml::de::DeTable<'de>>) -> Result<Self, toml::de::Error> {
|
fn deserialize_over_with<'de>(mut self, table: toml::Spanned<toml::de::DeTable<'de>>) -> Result<Self, toml::de::Error> {
|
||||||
use serde::{Deserialize, de::IntoDeserializer};
|
use yazi_shim::toml::{DeserializeOverHook, DeserializeOverWith, deserialize_spanned};
|
||||||
|
|
||||||
let mut table = table.into_inner();
|
let mut table = table.into_inner();
|
||||||
#(#assignments)*
|
#(#assignments)*
|
||||||
|
|
||||||
Ok(self)
|
self.deserialize_over_hook()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,20 +67,30 @@ pub fn deserialize_over1(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
#[proc_macro_derive(DeserializeOver2)]
|
#[proc_macro_derive(DeserializeOver2)]
|
||||||
pub fn deserialize_over2(input: TokenStream) -> TokenStream {
|
pub fn deserialize_over2(input: TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);
|
let DeriveInput { ident, generics, data, .. } = parse_macro_input!(input as DeriveInput);
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
let assignments = match data {
|
let assignments = match data {
|
||||||
Data::Struct(struct_) => match struct_.fields {
|
Data::Struct(struct_) => match struct_.fields {
|
||||||
Fields::Named(fields) => {
|
Fields::Named(fields) => {
|
||||||
let mut assignments = Vec::with_capacity(fields.named.len());
|
let mut assignments = Vec::with_capacity(fields.named.len());
|
||||||
|
|
||||||
for field in fields.named {
|
for (index, field) in fields.named.into_iter().enumerate() {
|
||||||
let field_ident = field.ident;
|
let field_ident = field.ident;
|
||||||
let field_name = field_ident.as_ref().unwrap().to_string();
|
let field_name = field_ident.as_ref().unwrap().to_string();
|
||||||
|
let field_ty = field.ty;
|
||||||
|
let field_attrs = field.attrs;
|
||||||
|
let field_helper = format_ident!("__DeserializeOver2Field{index}");
|
||||||
|
|
||||||
assignments.push(quote! {
|
assignments.push(quote! {
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct #field_helper #impl_generics (
|
||||||
|
#(#field_attrs)*
|
||||||
|
#field_ty
|
||||||
|
) #where_clause;
|
||||||
|
|
||||||
if let Some(value) = table.remove(#field_name) {
|
if let Some(value) = table.remove(#field_name) {
|
||||||
self.#field_ident = <_>::deserialize(value.into_deserializer())?;
|
self.#field_ident = deserialize_spanned::<#field_helper #ty_generics>(value)?.0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +103,9 @@ pub fn deserialize_over2(input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #ident {
|
impl #impl_generics yazi_shim::toml::DeserializeOverWith for #ident #ty_generics #where_clause {
|
||||||
pub(crate) fn deserialize_over_with<'de>(mut self, table: toml::Spanned<toml::de::DeTable<'de>>) -> Result<Self, toml::de::Error> {
|
fn deserialize_over_with<'de>(mut self, table: toml::Spanned<toml::de::DeTable<'de>>) -> Result<Self, toml::de::Error> {
|
||||||
use serde::{Deserialize, de::IntoDeserializer};
|
use yazi_shim::toml::deserialize_spanned;
|
||||||
|
|
||||||
let mut table = table.into_inner();
|
let mut table = table.into_inner();
|
||||||
#(#assignments)*
|
#(#assignments)*
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,25 @@ yazi-codegen = { path = "../yazi-codegen", version = "26.2.2" }
|
||||||
yazi-fs = { path = "../yazi-fs", version = "26.2.2" }
|
yazi-fs = { path = "../yazi-fs", version = "26.2.2" }
|
||||||
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
yazi-macro = { path = "../yazi-macro", version = "26.2.2" }
|
||||||
yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
yazi-shared = { path = "../yazi-shared", version = "26.2.2" }
|
||||||
|
yazi-shim = { path = "../yazi-shim", version = "26.2.2" }
|
||||||
yazi-tty = { path = "../yazi-tty", version = "26.2.2" }
|
yazi-tty = { path = "../yazi-tty", version = "26.2.2" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
bitflags = { workspace = true }
|
arc-swap = { workspace = true }
|
||||||
crossterm = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
globset = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
globset = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
ratatui = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
regex = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
serde = { workspace = true }
|
regex = { workspace = true }
|
||||||
strum = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
serde_with = { workspace = true }
|
||||||
toml = { workspace = true }
|
strum = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
toml = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,10 @@ suppress_preload = false
|
||||||
|
|
||||||
[plugin]
|
[plugin]
|
||||||
fetchers = [
|
fetchers = [
|
||||||
# Mimetype
|
# MIME-type
|
||||||
{ id = "mime", url = "*/", run = "mime.dir", prio = "high" },
|
{ url = "*/", run = "mime.dir", prio = "high", group = "mime" },
|
||||||
{ id = "mime", url = "local://*", run = "mime.local", prio = "high" },
|
{ url = "local://*", run = "mime.local", prio = "high", group = "mime" },
|
||||||
{ id = "mime", url = "remote://*", run = "mime.remote", prio = "high" },
|
{ url = "remote://*", run = "mime.remote", prio = "high", group = "mime" },
|
||||||
]
|
]
|
||||||
spotters = [
|
spotters = [
|
||||||
# Multi-file
|
# Multi-file
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::Style;
|
use crate::Style;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
#[serde(flatten)]
|
||||||
pub style: Style,
|
pub style: Style,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,38 @@
|
||||||
use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock};
|
use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock};
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Deserializer, de};
|
||||||
|
use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany};
|
||||||
use yazi_shared::{Layer, Source, event::Action};
|
use yazi_shared::{Layer, Source, event::Action};
|
||||||
|
|
||||||
use super::Key;
|
use super::Key;
|
||||||
use crate::Platform;
|
use crate::{Mixable, Platform};
|
||||||
|
|
||||||
static RE: OnceLock<Regex> = OnceLock::new();
|
static RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
pub struct Chord {
|
pub struct Chord<const L: u8 = { Layer::App as u8 }> {
|
||||||
#[serde(deserialize_with = "super::deserialize_on")]
|
#[serde(deserialize_with = "deserialize_on")]
|
||||||
pub on: Vec<Key>,
|
pub on: Vec<Key>,
|
||||||
#[serde(deserialize_with = "super::deserialize_run")]
|
#[serde(deserialize_with = "deserialize_run::<L, _>")]
|
||||||
pub run: Vec<Action>,
|
pub run: Vec<Action>,
|
||||||
pub desc: Option<String>,
|
#[serde(default)]
|
||||||
|
pub desc: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub r#for: Platform,
|
pub r#for: Platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Chord {
|
impl<const L: u8> PartialEq for Chord<L> {
|
||||||
fn eq(&self, other: &Self) -> bool { self.on == other.on }
|
fn eq(&self, other: &Self) -> bool { self.on == other.on }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Chord {}
|
impl<const L: u8> Eq for Chord<L> {}
|
||||||
|
|
||||||
impl Hash for Chord {
|
impl<const L: u8> Hash for Chord<L> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) { self.on.hash(state) }
|
fn hash<H: Hasher>(&self, state: &mut H) { self.on.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chord {
|
impl<const L: u8> Chord<L> {
|
||||||
pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() }
|
pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() }
|
||||||
|
|
||||||
pub fn run(&self) -> String {
|
pub fn run(&self) -> String {
|
||||||
|
|
@ -41,7 +42,9 @@ impl Chord {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn desc(&self) -> Option<Cow<'_, str>> {
|
pub fn desc(&self) -> Option<Cow<'_, str>> {
|
||||||
self.desc.as_ref().map(|s| RE.get_or_init(|| Regex::new(r"\s+").unwrap()).replace_all(s, " "))
|
Some(&self.desc)
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(|s| RE.get_or_init(|| Regex::new(r"\s+").unwrap()).replace_all(s, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn desc_or_run(&self) -> Cow<'_, str> { self.desc().unwrap_or_else(|| self.run().into()) }
|
pub fn desc_or_run(&self) -> Cow<'_, str> { self.desc().unwrap_or_else(|| self.run().into()) }
|
||||||
|
|
@ -59,14 +62,40 @@ impl Chord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chord {
|
impl<const L: u8> Mixable for Chord<L> {
|
||||||
pub(super) fn reshape(mut self, layer: Layer) -> Result<Self> {
|
fn filter(&self) -> bool { self.r#for.matches() && !self.noop() }
|
||||||
for action in &mut self.run {
|
}
|
||||||
action.source = Source::Key;
|
|
||||||
if action.layer == Default::default() {
|
fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
|
||||||
action.layer = layer;
|
where
|
||||||
}
|
D: Deserializer<'de>,
|
||||||
}
|
{
|
||||||
Ok(self)
|
let keys: Vec<Key> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
|
||||||
}
|
if keys.is_empty() {
|
||||||
|
return Err(de::Error::custom("'on' cannot be empty"));
|
||||||
|
}
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result<Vec<Action>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
|
||||||
|
if actions.is_empty() {
|
||||||
|
return Err(de::Error::custom("'run' cannot be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(layer) = Layer::from_repr(L) else {
|
||||||
|
return Err(de::Error::custom(format!("invalid keymap layer const: {L}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
for action in &mut actions {
|
||||||
|
action.source = Source::Key;
|
||||||
|
if action.layer == Default::default() {
|
||||||
|
action.layer = layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(actions)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,25 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use yazi_shared::event::ActionCow;
|
use yazi_shared::{Layer, event::ActionCow};
|
||||||
|
|
||||||
use super::Chord;
|
use super::Chord;
|
||||||
use crate::Platform;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ChordCow {
|
pub enum ChordCow<const L: u8 = { Layer::App as u8 }> {
|
||||||
Owned(Chord),
|
Owned(Chord<L>),
|
||||||
Borrowed(&'static Chord),
|
Borrowed(&'static Chord<L>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Chord> for ChordCow {
|
impl<const L: u8> From<Chord<L>> for ChordCow<L> {
|
||||||
fn from(c: Chord) -> Self { Self::Owned(c) }
|
fn from(c: Chord<L>) -> Self { Self::Owned(c) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&'static Chord> for ChordCow {
|
impl<const L: u8> From<&'static Chord<L>> for ChordCow<L> {
|
||||||
fn from(c: &'static Chord) -> Self { Self::Borrowed(c) }
|
fn from(c: &'static Chord<L>) -> Self { Self::Borrowed(c) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ChordCow {
|
impl<const L: u8> Deref for ChordCow<L> {
|
||||||
type Target = Chord;
|
type Target = Chord<L>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -30,14 +29,11 @@ impl Deref for ChordCow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChordCow {
|
impl<const L: u8> Default for ChordCow<L> {
|
||||||
fn default() -> Self {
|
fn default() -> Self { Self::Owned(Chord::default()) }
|
||||||
const C: &Chord = &Chord { on: vec![], run: vec![], desc: None, r#for: Platform::All };
|
|
||||||
Self::Borrowed(C)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChordCow {
|
impl<const L: u8> ChordCow<L> {
|
||||||
pub fn into_seq(self) -> Vec<ActionCow> {
|
pub fn into_seq(self) -> Vec<ActionCow> {
|
||||||
match self {
|
match self {
|
||||||
Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(),
|
Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(),
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
use std::{fmt, str::FromStr};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use serde::{Deserializer, de::{self, Visitor}};
|
|
||||||
use yazi_shared::event::Action;
|
|
||||||
|
|
||||||
use crate::keymap::Key;
|
|
||||||
|
|
||||||
pub(super) fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct OnVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for OnVisitor {
|
|
||||||
type Value = Vec<Key>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a `on` string or array of strings within keymap.toml")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: de::SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut keys = Vec::with_capacity(seq.size_hint().unwrap_or(0));
|
|
||||||
while let Some(value) = &seq.next_element::<String>()? {
|
|
||||||
keys.push(Key::from_str(value).map_err(de::Error::custom)?);
|
|
||||||
}
|
|
||||||
if keys.is_empty() {
|
|
||||||
return Err(de::Error::custom("`on` within keymap.toml cannot be empty"));
|
|
||||||
}
|
|
||||||
Ok(keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
Ok(vec![Key::from_str(value).map_err(de::Error::custom)?])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_any(OnVisitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn deserialize_run<'de, D>(deserializer: D) -> Result<Vec<Action>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct RunVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for RunVisitor {
|
|
||||||
type Value = Vec<Action>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a `run` string or array of strings within keymap.toml")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: de::SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut actions = Vec::with_capacity(seq.size_hint().unwrap_or(0));
|
|
||||||
while let Some(value) = &seq.next_element::<String>()? {
|
|
||||||
actions.push(Action::from_str(value).map_err(de::Error::custom)?);
|
|
||||||
}
|
|
||||||
if actions.is_empty() {
|
|
||||||
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
|
|
||||||
}
|
|
||||||
Ok(actions)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
Ok(vec![Action::from_str(value).map_err(de::Error::custom)?])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_any(RunVisitor)
|
|
||||||
}
|
|
||||||
|
|
@ -8,28 +8,28 @@ use super::{Chord, KeymapRules};
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver, DeserializeOver1)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver1)]
|
||||||
pub struct Keymap {
|
pub struct Keymap {
|
||||||
pub mgr: KeymapRules,
|
pub mgr: KeymapRules<{ Layer::Mgr as u8 }>,
|
||||||
pub tasks: KeymapRules,
|
pub tasks: KeymapRules<{ Layer::Tasks as u8 }>,
|
||||||
pub spot: KeymapRules,
|
pub spot: KeymapRules<{ Layer::Spot as u8 }>,
|
||||||
pub pick: KeymapRules,
|
pub pick: KeymapRules<{ Layer::Pick as u8 }>,
|
||||||
pub input: KeymapRules,
|
pub input: KeymapRules<{ Layer::Input as u8 }>,
|
||||||
pub confirm: KeymapRules,
|
pub confirm: KeymapRules<{ Layer::Confirm as u8 }>,
|
||||||
pub help: KeymapRules,
|
pub help: KeymapRules<{ Layer::Help as u8 }>,
|
||||||
pub cmp: KeymapRules,
|
pub cmp: KeymapRules<{ Layer::Cmp as u8 }>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keymap {
|
impl Keymap {
|
||||||
pub fn get(&self, layer: Layer) -> &[Chord] {
|
pub fn get(&self, layer: Layer) -> &[Chord] {
|
||||||
match layer {
|
match layer {
|
||||||
Layer::App => &[],
|
Layer::App => &[],
|
||||||
Layer::Mgr => &self.mgr,
|
Layer::Mgr => self.mgr.as_erased_slice(),
|
||||||
Layer::Tasks => &self.tasks,
|
Layer::Tasks => self.tasks.as_erased_slice(),
|
||||||
Layer::Spot => &self.spot,
|
Layer::Spot => self.spot.as_erased_slice(),
|
||||||
Layer::Pick => &self.pick,
|
Layer::Pick => self.pick.as_erased_slice(),
|
||||||
Layer::Input => &self.input,
|
Layer::Input => self.input.as_erased_slice(),
|
||||||
Layer::Confirm => &self.confirm,
|
Layer::Confirm => self.confirm.as_erased_slice(),
|
||||||
Layer::Help => &self.help,
|
Layer::Help => self.help.as_erased_slice(),
|
||||||
Layer::Cmp => &self.cmp,
|
Layer::Cmp => self.cmp.as_erased_slice(),
|
||||||
Layer::Which => &[],
|
Layer::Which => &[],
|
||||||
Layer::Notify => &[],
|
Layer::Notify => &[],
|
||||||
}
|
}
|
||||||
|
|
@ -42,17 +42,4 @@ impl Keymap {
|
||||||
ok_or_not_found(std::fs::read_to_string(&p))
|
ok_or_not_found(std::fs::read_to_string(&p))
|
||||||
.with_context(|| format!("Failed to read keymap {p:?}"))
|
.with_context(|| format!("Failed to read keymap {p:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reshape(self) -> Result<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
mgr: self.mgr.reshape(Layer::Mgr)?,
|
|
||||||
tasks: self.tasks.reshape(Layer::Tasks)?,
|
|
||||||
spot: self.spot.reshape(Layer::Spot)?,
|
|
||||||
pick: self.pick.reshape(Layer::Pick)?,
|
|
||||||
input: self.input.reshape(Layer::Input)?,
|
|
||||||
confirm: self.confirm.reshape(Layer::Confirm)?,
|
|
||||||
help: self.help.reshape(Layer::Help)?,
|
|
||||||
cmp: self.cmp.reshape(Layer::Cmp)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(chord cow deserializers key keymap rules);
|
yazi_macro::mod_flat!(chord cow key keymap rules);
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,52 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
use yazi_shared::Layer;
|
use yazi_shim::toml::DeserializeOverHook;
|
||||||
|
|
||||||
use super::Chord;
|
use super::{Chord, Key};
|
||||||
use crate::{Preset, keymap::Key};
|
use crate::mix;
|
||||||
|
|
||||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||||
pub struct KeymapRules {
|
pub struct KeymapRules<const L: u8> {
|
||||||
pub keymap: Vec<Chord>,
|
keymap: Vec<Chord<L>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_keymap: Vec<Chord>,
|
prepend_keymap: Vec<Chord<L>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_keymap: Vec<Chord>,
|
append_keymap: Vec<Chord<L>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for KeymapRules {
|
impl<const L: u8> Deref for KeymapRules<L> {
|
||||||
type Target = Vec<Chord>;
|
type Target = [Chord];
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.keymap }
|
fn deref(&self) -> &Self::Target { self.as_erased_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeymapRules {
|
impl<const L: u8> KeymapRules<L> {
|
||||||
pub(crate) fn reshape(self, layer: Layer) -> Result<Self> {
|
pub(super) fn as_erased_slice(&self) -> &[Chord] {
|
||||||
|
// Safety: `Chord<L>` only changes deserialization behavior; the const parameter
|
||||||
|
// does not participate in layout, so a shared slice can be reinterpreted as
|
||||||
|
// the default `Chord` view.
|
||||||
|
unsafe { &*(self.keymap.as_slice() as *const [Chord<L>] as *const [Chord]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const L: u8> DeserializeOverHook for KeymapRules<L> {
|
||||||
|
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn on(Chord { on, .. }: &Chord) -> [Key; 2] {
|
fn on<const L: u8>(Chord { on, .. }: &Chord<L>) -> [Key; 2] {
|
||||||
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
|
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
|
||||||
}
|
}
|
||||||
|
|
||||||
let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect();
|
let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect();
|
||||||
let b_seen: HashSet<_> = self.keymap.iter().map(on).collect();
|
let b_seen: HashSet<_> = self.keymap.iter().map(on).collect();
|
||||||
|
|
||||||
let keymap = Preset::mix(
|
let keymap = mix(
|
||||||
self.prepend_keymap,
|
self.prepend_keymap,
|
||||||
self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
|
self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
|
||||||
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
|
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
|
||||||
)
|
);
|
||||||
.filter(|chord| !chord.noop() && chord.r#for.matches())
|
|
||||||
.map(|chord| chord.reshape(layer))
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
|
|
||||||
Ok(Self { keymap, ..Default::default() })
|
Ok(Self { keymap, ..Default::default() })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs);
|
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(icon layout pattern platform preset priority style utils yazi);
|
yazi_macro::mod_flat!(icon layout mixing pattern platform preset priority selectable selector style utils yazi);
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use yazi_shared::{RoCell, SyncCell};
|
use yazi_shared::{RoCell, SyncCell};
|
||||||
|
use yazi_shim::toml::{DeserializeOver, DeserializeOverWith};
|
||||||
use yazi_tty::TTY;
|
use yazi_tty::TTY;
|
||||||
|
|
||||||
pub static YAZI: RoCell<yazi::Yazi> = RoCell::new();
|
pub static YAZI: RoCell<yazi::Yazi> = RoCell::new();
|
||||||
|
|
@ -29,8 +30,8 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
||||||
keymap = keymap.deserialize_over(&keymap::Keymap::read()?)?;
|
keymap = keymap.deserialize_over(&keymap::Keymap::read()?)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
YAZI.init(yazi.reshape()?);
|
YAZI.init(yazi);
|
||||||
KEYMAP.init(keymap.reshape()?);
|
KEYMAP.init(keymap);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
yazi-config/src/mgr/linemode.rs
Normal file
23
yazi-config/src/mgr/linemode.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, de};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MgrLinemode(String);
|
||||||
|
|
||||||
|
impl Deref for MgrLinemode {
|
||||||
|
type Target = String;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for MgrLinemode {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
if s.is_empty() || s.len() > 20 {
|
||||||
|
return Err(de::Error::custom("linemode must be between 1 and 20 characters."));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use anyhow::{Result, bail};
|
use anyhow::Result;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
use yazi_fs::{SortBy, SortFallback};
|
use yazi_fs::{SortBy, SortFallback};
|
||||||
use yazi_shared::SyncCell;
|
use yazi_shared::SyncCell;
|
||||||
|
|
||||||
use super::{MgrRatio, MouseEvents};
|
use super::{MgrRatio, MouseEvents};
|
||||||
|
use crate::mgr::MgrLinemode;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, DeserializeOver2)]
|
#[derive(Debug, Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Mgr {
|
pub struct Mgr {
|
||||||
pub ratio: SyncCell<MgrRatio>,
|
pub ratio: SyncCell<MgrRatio>,
|
||||||
|
|
||||||
|
|
@ -19,19 +20,9 @@ pub struct Mgr {
|
||||||
pub sort_fallback: SyncCell<SortFallback>,
|
pub sort_fallback: SyncCell<SortFallback>,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
pub linemode: String,
|
pub linemode: MgrLinemode,
|
||||||
pub show_hidden: SyncCell<bool>,
|
pub show_hidden: SyncCell<bool>,
|
||||||
pub show_symlink: SyncCell<bool>,
|
pub show_symlink: SyncCell<bool>,
|
||||||
pub scrolloff: SyncCell<u8>,
|
pub scrolloff: SyncCell<u8>,
|
||||||
pub mouse_events: SyncCell<MouseEvents>,
|
pub mouse_events: SyncCell<MouseEvents>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mgr {
|
|
||||||
pub(crate) fn reshape(self) -> Result<Self> {
|
|
||||||
if self.linemode.is_empty() || self.linemode.len() > 20 {
|
|
||||||
bail!("[mgr].linemode must be between 1 and 20 characters.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(mgr mouse ratio);
|
yazi_macro::mod_flat!(linemode mgr mouse ratio);
|
||||||
|
|
|
||||||
77
yazi-config/src/mixing.rs
Normal file
77
yazi-config/src/mixing.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
|
pub(crate) trait Mixable {
|
||||||
|
fn filter(&self) -> bool { true }
|
||||||
|
|
||||||
|
fn any_file(&self) -> bool { false }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { false }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Mixable> Mixable for Arc<T> {
|
||||||
|
fn filter(&self) -> bool { (**self).filter() }
|
||||||
|
|
||||||
|
fn any_file(&self) -> bool { (**self).any_file() }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { (**self).any_dir() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mix<E, A, B, C>(a: A, b: B, c: C) -> Vec<E>
|
||||||
|
where
|
||||||
|
E: Mixable,
|
||||||
|
A: IntoIterator,
|
||||||
|
A::Item: Into<E> + Mixable,
|
||||||
|
B: IntoIterator,
|
||||||
|
B::Item: Into<E> + Mixable,
|
||||||
|
C: IntoIterator,
|
||||||
|
C::Item: Into<E> + Mixable,
|
||||||
|
{
|
||||||
|
fn dedup<E, I>(it: I, any_file: &mut bool, any_dir: &mut bool) -> impl Iterator<Item = E>
|
||||||
|
where
|
||||||
|
I: Iterator,
|
||||||
|
I::Item: Into<E> + Mixable,
|
||||||
|
{
|
||||||
|
it.filter(move |x| {
|
||||||
|
if !x.filter() {
|
||||||
|
false
|
||||||
|
} else if x.any_file() && mem::replace(any_file, true) {
|
||||||
|
false
|
||||||
|
} else if x.any_dir() && mem::replace(any_dir, true) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (a, b, c) = (a.into_iter(), b.into_iter(), c.into_iter());
|
||||||
|
let mut mixed = Vec::with_capacity(a.size_hint().0 + b.size_hint().0 + c.size_hint().0);
|
||||||
|
|
||||||
|
let (mut a_any_file, mut a_any_dir) = (false, false);
|
||||||
|
mixed.extend(dedup(a, &mut a_any_file, &mut a_any_dir));
|
||||||
|
let a_len = mixed.len();
|
||||||
|
|
||||||
|
mixed.extend(dedup(b, &mut a_any_file, &mut a_any_dir));
|
||||||
|
let b_len = mixed.len();
|
||||||
|
|
||||||
|
let (mut c_any_file, mut c_any_dir) = (false, false);
|
||||||
|
mixed.extend(dedup(c, &mut c_any_file, &mut c_any_dir));
|
||||||
|
|
||||||
|
if c_any_file || c_any_dir {
|
||||||
|
let mut i = 0;
|
||||||
|
mixed.retain(|x| {
|
||||||
|
let in_b = (a_len..b_len).contains(&i);
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if in_b && c_any_file && x.any_file() {
|
||||||
|
return false;
|
||||||
|
} else if in_b && c_any_dir && x.any_dir() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mixed
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,11 @@ use anyhow::Result;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
|
use yazi_fs::{File, cha::ChaType};
|
||||||
use yazi_shared::url::AsUrl;
|
use yazi_shared::url::AsUrl;
|
||||||
|
use yazi_shim::toml::DeserializeOverHook;
|
||||||
|
|
||||||
use crate::{Preset, open::OpenRule};
|
use crate::{Selectable, mix, open::OpenRule};
|
||||||
|
|
||||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||||
pub struct Open {
|
pub struct Open {
|
||||||
|
|
@ -24,33 +26,40 @@ impl Deref for Open {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open {
|
impl Open {
|
||||||
pub fn all<'a, 'b, U, M>(&'a self, url: U, mime: M) -> impl Iterator<Item = &'a str> + 'b
|
pub fn all<'a>(&'a self, file: &File, mime: &str) -> impl Iterator<Item = &'a str> {
|
||||||
where
|
|
||||||
'a: 'b,
|
|
||||||
U: AsUrl + 'b,
|
|
||||||
M: AsRef<str> + 'b,
|
|
||||||
{
|
|
||||||
let is_dir = mime.as_ref().starts_with("folder/");
|
|
||||||
self
|
self
|
||||||
.rules
|
.rules
|
||||||
.iter()
|
.iter()
|
||||||
.find(move |&r| {
|
.find(move |&rule| rule.matches(file, mime))
|
||||||
r.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
|
|
||||||
|| r.url.as_ref().is_some_and(|p| p.match_url(url.as_url(), is_dir))
|
|
||||||
})
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|r| &r.r#use)
|
.flat_map(|r| &r.r#use)
|
||||||
.map(String::as_str)
|
.map(String::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn common<'a, 'b, U, M>(&'a self, targets: &'b [(U, M)]) -> IndexSet<&'a str>
|
pub fn all_dummy<'a, U, M>(&'a self, url: U, mime: M) -> impl Iterator<Item = &'a str>
|
||||||
where
|
where
|
||||||
&'b U: AsUrl,
|
U: AsUrl,
|
||||||
M: AsRef<str>,
|
M: AsRef<str>,
|
||||||
{
|
{
|
||||||
|
let mime = mime.as_ref();
|
||||||
|
let file = File::from_dummy(
|
||||||
|
url.as_url().to_owned(),
|
||||||
|
Some(if mime.starts_with("folder/") { ChaType::Dir } else { ChaType::File }),
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
.rules
|
||||||
|
.iter()
|
||||||
|
.find(move |&rule| rule.matches(&file, mime))
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|r| &r.r#use)
|
||||||
|
.map(String::as_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn common<'a>(&'a self, targets: &[(File, &str)]) -> IndexSet<&'a str> {
|
||||||
let each: Vec<IndexSet<&str>> = targets
|
let each: Vec<IndexSet<&str>> = targets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(u, m)| self.all(u, m).collect::<IndexSet<_>>())
|
.map(|(file, mime)| self.all(file, mime).collect::<IndexSet<_>>())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
@ -60,17 +69,8 @@ impl Open {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open {
|
impl DeserializeOverHook for Open {
|
||||||
pub(crate) fn reshape(self) -> Result<Self> {
|
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||||
let any_file = self.append_rules.iter().any(|r| r.any_file());
|
Ok(Self { rules: mix(self.prepend_rules, self.rules, self.append_rules), ..Default::default() })
|
||||||
let any_dir = self.append_rules.iter().any(|r| r.any_dir());
|
|
||||||
|
|
||||||
let it =
|
|
||||||
self.rules.into_iter().filter(|r| !(any_file && r.any_file() || any_dir && r.any_dir()));
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
rules: Preset::mix(self.prepend_rules, it, self.append_rules).collect(),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,25 @@
|
||||||
use std::fmt;
|
use serde::Deserialize;
|
||||||
|
use serde_with::{OneOrMany, formats::PreferOne, serde_as};
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, de::{self, Visitor}};
|
use crate::{Mixable, Pattern, Selectable, Selector};
|
||||||
|
|
||||||
use crate::pattern::Pattern;
|
#[serde_as]
|
||||||
|
#[derive(Deserialize)]
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct OpenRule {
|
pub struct OpenRule {
|
||||||
pub url: Option<Pattern>,
|
#[serde(flatten)]
|
||||||
pub mime: Option<Pattern>,
|
pub selector: Selector,
|
||||||
#[serde(deserialize_with = "OpenRule::deserialize")]
|
#[serde_as(as = "OneOrMany<_, PreferOne>")]
|
||||||
pub r#use: Vec<String>,
|
pub r#use: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenRule {
|
impl Selectable for OpenRule {
|
||||||
#[inline]
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
pub fn any_file(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_file()) }
|
|
||||||
|
|
||||||
#[inline]
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
pub fn any_dir(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_dir()) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenRule {
|
impl Mixable for OpenRule {
|
||||||
fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
fn any_file(&self) -> bool { self.selector.any_file() }
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct UseVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for UseVisitor {
|
fn any_dir(&self) -> bool { self.selector.any_dir() }
|
||||||
type Value = Vec<String>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a string, or array of strings")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: de::SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut uses = Vec::with_capacity(seq.size_hint().unwrap_or(0));
|
|
||||||
while let Some(use_) = seq.next_element::<String>()? {
|
|
||||||
uses.push(use_);
|
|
||||||
}
|
|
||||||
Ok(uses)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
Ok(vec![value.to_owned()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
Ok(vec![v])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_any(UseVisitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
use std::{mem, ops::Deref};
|
use std::ops::Deref;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use indexmap::IndexSet;
|
use serde::Deserialize;
|
||||||
use serde::{Deserialize, de::IntoDeserializer};
|
|
||||||
use toml::{Spanned, de::DeTable};
|
use toml::{Spanned, de::DeTable};
|
||||||
use yazi_codegen::DeserializeOver;
|
use yazi_shim::toml::{DeserializeOverHook, DeserializeOverWith, deserialize_spanned};
|
||||||
|
|
||||||
use super::OpenerRule;
|
use super::OpenerRule;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, DeserializeOver)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Opener(HashMap<String, Vec<OpenerRule>>);
|
pub struct Opener(HashMap<String, Vec<OpenerRule>>);
|
||||||
|
|
||||||
impl Deref for Opener {
|
impl Deref for Opener {
|
||||||
|
|
@ -41,27 +39,24 @@ impl Opener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opener {
|
impl DeserializeOverHook for Opener {
|
||||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
fn deserialize_over_hook(mut self) -> Result<Self, toml::de::Error> {
|
||||||
for rules in self.0.values_mut() {
|
for rules in self.0.values_mut() {
|
||||||
*rules = mem::take(rules)
|
rules.retain(|r| r.r#for.matches());
|
||||||
.into_iter()
|
rules.iter_mut().for_each(|r| r.fill());
|
||||||
.filter(|r| r.r#for.matches())
|
}
|
||||||
.map(|r| r.reshape())
|
|
||||||
.collect::<Result<IndexSet<_>>>()?
|
Ok(self)
|
||||||
.into_iter()
|
}
|
||||||
.collect();
|
}
|
||||||
}
|
|
||||||
|
impl DeserializeOverWith for Opener {
|
||||||
Ok(self)
|
fn deserialize_over_with<'de>(
|
||||||
}
|
mut self,
|
||||||
|
table: Spanned<DeTable<'de>>,
|
||||||
pub(crate) fn deserialize_over_with<'de>(
|
) -> Result<Self, toml::de::Error> {
|
||||||
mut self,
|
for (key, value) in table.into_inner() {
|
||||||
table: Spanned<DeTable<'de>>,
|
self.0.insert(key.into_inner().into_owned(), deserialize_spanned(value)?);
|
||||||
) -> Result<Self, toml::de::Error> {
|
|
||||||
for (key, value) in table.into_inner() {
|
|
||||||
self.0.insert(key.into_inner().into_owned(), <_>::deserialize(value.into_deserializer())?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use anyhow::{Result, bail};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_fs::Splatter;
|
use yazi_fs::Splatter;
|
||||||
|
use yazi_shared::NonEmptyString;
|
||||||
|
|
||||||
use crate::Platform;
|
use crate::Platform;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct OpenerRule {
|
pub struct OpenerRule {
|
||||||
pub run: String,
|
pub run: NonEmptyString,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub block: bool,
|
pub block: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -29,14 +29,8 @@ impl OpenerRule {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenerRule {
|
|
||||||
pub(super) fn reshape(mut self) -> Result<Self> {
|
|
||||||
if self.run.is_empty() {
|
|
||||||
bail!("[open].rules.*.run cannot be empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
pub(super) fn fill(&mut self) {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
self.spread =
|
self.spread =
|
||||||
|
|
@ -46,7 +40,5 @@ impl OpenerRule {
|
||||||
{
|
{
|
||||||
self.spread = Splatter::<()>::spread(&self.run) || self.run.contains("%*");
|
self.spread = Splatter::<()>::spread(&self.run) || self.run.contains("%*");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use globset::{Candidate, GlobBuilder};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_shared::{scheme::SchemeKind, url::AsUrl};
|
use yazi_shared::{scheme::SchemeKind, url::AsUrl};
|
||||||
|
|
||||||
|
use crate::Mixable;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
pub struct Pattern {
|
pub struct Pattern {
|
||||||
|
|
@ -58,12 +60,6 @@ impl Pattern {
|
||||||
pub fn match_mime(&self, mime: impl AsRef<str>) -> bool {
|
pub fn match_mime(&self, mime: impl AsRef<str>) -> bool {
|
||||||
self.is_star || (!mime.as_ref().is_empty() && self.inner.is_match(mime.as_ref()))
|
self.is_star || (!mime.as_ref().is_empty() && self.inner.is_match(mime.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn any_file(&self) -> bool { self.is_star && !self.is_dir }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn any_dir(&self) -> bool { self.is_star && self.is_dir }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Pattern {
|
impl FromStr for Pattern {
|
||||||
|
|
@ -102,12 +98,19 @@ impl FromStr for Pattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: remove
|
||||||
impl TryFrom<String> for Pattern {
|
impl TryFrom<String> for Pattern {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(s.as_str()) }
|
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(s.as_str()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mixable for Pattern {
|
||||||
|
fn any_file(&self) -> bool { self.is_star && !self.is_dir }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { self.is_star && self.is_dir }
|
||||||
|
}
|
||||||
|
|
||||||
// --- Scheme
|
// --- Scheme
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum PatternScheme {
|
enum PatternScheme {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,34 @@
|
||||||
use serde::Deserialize;
|
use std::ops::Deref;
|
||||||
use yazi_fs::File;
|
|
||||||
use yazi_shared::event::Action;
|
|
||||||
|
|
||||||
use crate::{Pattern, Priority};
|
use serde::Deserialize;
|
||||||
|
use yazi_shared::{Id, event::Action};
|
||||||
|
|
||||||
|
use crate::{Mixable, Pattern, Priority, Selectable, Selector, plugin::fetcher_id};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Fetcher {
|
pub struct Fetcher {
|
||||||
|
#[serde(skip, default = "fetcher_id")]
|
||||||
|
pub id: Id,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub idx: u8,
|
pub idx: u8,
|
||||||
|
#[serde(flatten)]
|
||||||
pub id: String,
|
pub selector: Selector,
|
||||||
pub url: Option<Pattern>,
|
pub run: Action,
|
||||||
pub mime: Option<Pattern>,
|
|
||||||
pub run: Action,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub prio: Priority,
|
pub prio: Priority,
|
||||||
|
pub group: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fetcher {
|
impl Deref for Fetcher {
|
||||||
#[inline]
|
type Target = Action;
|
||||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
|
||||||
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
fn deref(&self) -> &Self::Target { &self.run }
|
||||||
|| self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Selectable for Fetcher {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixable for Fetcher {}
|
||||||
|
|
|
||||||
117
yazi-config/src/plugin/fetchers.rs
Normal file
117
yazi-config/src/plugin/fetchers.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
use std::{borrow::Cow, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tracing::warn;
|
||||||
|
use yazi_fs::File;
|
||||||
|
use yazi_shared::Id;
|
||||||
|
use yazi_shim::arc_swap::IntoPointee;
|
||||||
|
|
||||||
|
use super::{Fetcher, MAX_FETCHERS};
|
||||||
|
use crate::Selectable;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct Fetchers(ArcSwap<Vec<Arc<Fetcher>>>);
|
||||||
|
|
||||||
|
impl Deref for Fetchers {
|
||||||
|
type Target = ArcSwap<Vec<Arc<Fetcher>>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Arc<Fetcher>>> for Fetchers {
|
||||||
|
fn from(inner: Vec<Arc<Fetcher>>) -> Self { Self(inner.into_pointee()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<Vec<Arc<Fetcher>>>> for Fetchers {
|
||||||
|
fn from(inner: Arc<Vec<Arc<Fetcher>>>) -> Self { Self(inner.into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fetchers {
|
||||||
|
pub fn matches<'a>(&self, file: &'a File, mime: &'a str) -> FetcherMatcher<'a> {
|
||||||
|
self.matcher(Some(file), Some(mime))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matcher<'a, F, M>(&self, file: Option<F>, mime: Option<M>) -> FetcherMatcher<'a>
|
||||||
|
where
|
||||||
|
F: Into<Cow<'a, File>>,
|
||||||
|
M: Into<Cow<'a, str>>,
|
||||||
|
{
|
||||||
|
FetcherMatcher {
|
||||||
|
fetchers: self.load_full(),
|
||||||
|
file: file.map(Into::into),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mime(&self, files: Vec<File>) -> impl Iterator<Item = (Arc<Fetcher>, Vec<File>)> {
|
||||||
|
let mut tasks: [Vec<_>; MAX_FETCHERS as usize] = Default::default();
|
||||||
|
|
||||||
|
for file in files {
|
||||||
|
let found = self.matches(&file, "").find(|f| f.group == "mime");
|
||||||
|
if let Some(fetcher) = found {
|
||||||
|
tasks[fetcher.idx as usize].push(file);
|
||||||
|
} else {
|
||||||
|
warn!("No mime fetcher for {file:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetchers = self.load();
|
||||||
|
tasks.into_iter().enumerate().filter_map(move |(i, tasks)| {
|
||||||
|
if tasks.is_empty() { None } else { Some((fetchers[i].clone(), tasks)) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unwrap_unchecked(self) -> Vec<Arc<Fetcher>> {
|
||||||
|
Arc::try_unwrap(self.0.into_inner()).expect("unique fetchers arc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FetcherMatcher<'a> {
|
||||||
|
pub fetchers: Arc<Vec<Arc<Fetcher>>>,
|
||||||
|
pub id: Id,
|
||||||
|
pub file: Option<Cow<'a, File>>,
|
||||||
|
pub mime: Option<Cow<'a, str>>,
|
||||||
|
pub all: bool,
|
||||||
|
pub offset: usize,
|
||||||
|
pub seen: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Fetchers> for FetcherMatcher<'_> {
|
||||||
|
fn from(fetchers: &Fetchers) -> Self {
|
||||||
|
Self { fetchers: fetchers.load_full(), all: true, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetcherMatcher<'_> {
|
||||||
|
pub fn matches(&self, fetcher: &Fetcher) -> bool {
|
||||||
|
if self.all {
|
||||||
|
true
|
||||||
|
} else if self.id != Id::ZERO {
|
||||||
|
fetcher.id == self.id
|
||||||
|
} else {
|
||||||
|
fetcher.match_with(self.file.as_deref(), self.mime.as_deref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for FetcherMatcher<'_> {
|
||||||
|
type Item = Arc<Fetcher>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(fetcher) = self.fetchers.get(self.offset) {
|
||||||
|
self.offset += 1;
|
||||||
|
if !self.matches(fetcher) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.all || self.seen.insert(fetcher.group.clone()) {
|
||||||
|
return Some(fetcher.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
21
yazi-config/src/plugin/ids.rs
Normal file
21
yazi-config/src/plugin/ids.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
use yazi_shared::{Id, Ids};
|
||||||
|
|
||||||
|
pub fn fetcher_id() -> Id {
|
||||||
|
static IDS: Ids = Ids::new();
|
||||||
|
IDS.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preloader_id() -> Id {
|
||||||
|
static IDS: Ids = Ids::new();
|
||||||
|
IDS.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn previewer_id() -> Id {
|
||||||
|
static IDS: Ids = Ids::new();
|
||||||
|
IDS.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spotter_id() -> Id {
|
||||||
|
static IDS: Ids = Ids::new();
|
||||||
|
IDS.next()
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
yazi_macro::mod_flat!(fetcher plugin preloader previewer spotter);
|
yazi_macro::mod_flat!(fetcher fetchers ids plugin preloader preloaders previewer previewers spotter spotters);
|
||||||
|
|
||||||
pub const MAX_FETCHERS: u8 = 16;
|
pub const MAX_FETCHERS: u8 = 16;
|
||||||
pub const MAX_PRELOADERS: u8 = 16;
|
pub const MAX_PRELOADERS: u8 = 16;
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,69 @@
|
||||||
use anyhow::{Result, bail};
|
use std::sync::Arc;
|
||||||
use hashbrown::HashSet;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use tracing::warn;
|
|
||||||
use yazi_codegen::DeserializeOver2;
|
|
||||||
use yazi_fs::File;
|
|
||||||
|
|
||||||
use super::{Fetcher, Preloader, Previewer, Spotter};
|
use anyhow::Result;
|
||||||
use crate::{Preset, plugin::{MAX_FETCHERS, MAX_PRELOADERS}};
|
use serde::{Deserialize, de};
|
||||||
|
use yazi_codegen::DeserializeOver2;
|
||||||
|
use yazi_shim::toml::DeserializeOverHook;
|
||||||
|
|
||||||
|
use super::{Fetcher, Fetchers, Preloader, Preloaders, Previewer, Previewers, Spotter, Spotters};
|
||||||
|
use crate::{mix, plugin::{MAX_FETCHERS, MAX_PRELOADERS}};
|
||||||
|
|
||||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||||
pub struct Plugin {
|
pub struct Plugin {
|
||||||
pub fetchers: Vec<Fetcher>,
|
pub fetchers: Fetchers,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_fetchers: Vec<Fetcher>,
|
prepend_fetchers: Vec<Fetcher>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_fetchers: Vec<Fetcher>,
|
append_fetchers: Vec<Fetcher>,
|
||||||
|
|
||||||
pub spotters: Vec<Spotter>,
|
pub spotters: Spotters,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_spotters: Vec<Spotter>,
|
prepend_spotters: Vec<Spotter>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_spotters: Vec<Spotter>,
|
append_spotters: Vec<Spotter>,
|
||||||
|
|
||||||
pub preloaders: Vec<Preloader>,
|
pub preloaders: Preloaders,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_preloaders: Vec<Preloader>,
|
prepend_preloaders: Vec<Preloader>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_preloaders: Vec<Preloader>,
|
append_preloaders: Vec<Preloader>,
|
||||||
|
|
||||||
pub previewers: Vec<Previewer>,
|
pub previewers: Previewers,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_previewers: Vec<Previewer>,
|
prepend_previewers: Vec<Previewer>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_previewers: Vec<Previewer>,
|
append_previewers: Vec<Previewer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin {
|
impl DeserializeOverHook for Plugin {
|
||||||
pub fn fetchers<'a, 'b: 'a>(
|
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||||
&'b self,
|
let mut fetchers: Vec<Arc<Fetcher>> =
|
||||||
file: &'a File,
|
mix(self.prepend_fetchers, self.fetchers.unwrap_unchecked(), self.append_fetchers);
|
||||||
mime: &'a str,
|
let spotters: Vec<Arc<Spotter>> =
|
||||||
) -> impl Iterator<Item = &'b Fetcher> + 'a {
|
mix(self.prepend_spotters, self.spotters.unwrap_unchecked(), self.append_spotters);
|
||||||
let mut seen = HashSet::new();
|
let mut preloaders: Vec<Arc<Preloader>> =
|
||||||
self.fetchers.iter().filter(move |&f| {
|
mix(self.prepend_preloaders, self.preloaders.unwrap_unchecked(), self.append_preloaders);
|
||||||
if seen.contains(&f.id) || !f.matches(file, mime) {
|
let previewers: Vec<Arc<Previewer>> =
|
||||||
return false;
|
mix(self.prepend_previewers, self.previewers.unwrap_unchecked(), self.append_previewers);
|
||||||
}
|
|
||||||
seen.insert(&f.id);
|
|
||||||
true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mime_fetchers(&self, files: Vec<File>) -> impl Iterator<Item = (&Fetcher, Vec<File>)> {
|
if fetchers.len() > MAX_FETCHERS as usize {
|
||||||
let mut tasks: [Vec<_>; MAX_FETCHERS as usize] = Default::default();
|
Err(de::Error::custom(format!("Fetchers exceed the limit of {MAX_FETCHERS}")))?;
|
||||||
for f in files {
|
} else if preloaders.len() > MAX_PRELOADERS as usize {
|
||||||
let found = self.fetchers.iter().find(|&g| g.id == "mime" && g.matches(&f, ""));
|
Err(de::Error::custom(format!("Preloaders exceed the limit of {MAX_PRELOADERS}")))?;
|
||||||
if let Some(g) = found {
|
|
||||||
tasks[g.idx as usize].push(f);
|
|
||||||
} else {
|
|
||||||
warn!("No mime fetcher for {f:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.into_iter().enumerate().filter_map(|(i, tasks)| {
|
for (i, p) in fetchers.iter_mut().enumerate() {
|
||||||
if tasks.is_empty() { None } else { Some((&self.fetchers[i], tasks)) }
|
Arc::get_mut(p).ok_or_else(|| de::Error::custom("non-unique fetcher arc"))?.idx = i as u8;
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spotter(&self, file: &File, mime: &str) -> Option<&Spotter> {
|
|
||||||
self.spotters.iter().find(|&p| p.matches(file, mime))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn preloaders<'a, 'b: 'a>(
|
|
||||||
&'b self,
|
|
||||||
file: &'a File,
|
|
||||||
mime: &'a str,
|
|
||||||
) -> impl Iterator<Item = &'b Preloader> + 'a {
|
|
||||||
let mut next = true;
|
|
||||||
self.preloaders.iter().filter(move |&p| {
|
|
||||||
if !next || !p.matches(file, mime) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
next = p.next;
|
|
||||||
true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn previewer(&self, file: &File, mime: &str) -> Option<&Previewer> {
|
|
||||||
self.previewers.iter().find(|&p| p.matches(file, mime))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin {
|
|
||||||
// TODO: remove .retain() and .collect()
|
|
||||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
|
||||||
if self.append_spotters.iter().any(|r| r.any_file()) {
|
|
||||||
self.spotters.retain(|r| !r.any_file());
|
|
||||||
}
|
}
|
||||||
if self.append_spotters.iter().any(|r| r.any_dir()) {
|
for (i, p) in preloaders.iter_mut().enumerate() {
|
||||||
self.spotters.retain(|r| !r.any_dir());
|
Arc::get_mut(p).ok_or_else(|| de::Error::custom("non-unique preloader arc"))?.idx = i as u8;
|
||||||
}
|
|
||||||
if self.append_previewers.iter().any(|r| r.any_file()) {
|
|
||||||
self.previewers.retain(|r| !r.any_file());
|
|
||||||
}
|
|
||||||
if self.append_previewers.iter().any(|r| r.any_dir()) {
|
|
||||||
self.previewers.retain(|r| !r.any_dir());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fetchers =
|
|
||||||
Preset::mix(self.prepend_fetchers, self.fetchers, self.append_fetchers).collect();
|
|
||||||
self.spotters =
|
|
||||||
Preset::mix(self.prepend_spotters, self.spotters, self.append_spotters).collect();
|
|
||||||
self.preloaders =
|
|
||||||
Preset::mix(self.prepend_preloaders, self.preloaders, self.append_preloaders).collect();
|
|
||||||
self.previewers =
|
|
||||||
Preset::mix(self.prepend_previewers, self.previewers, self.append_previewers).collect();
|
|
||||||
|
|
||||||
if self.fetchers.len() > MAX_FETCHERS as usize {
|
|
||||||
bail!("Fetchers exceed the limit of {MAX_FETCHERS}");
|
|
||||||
} else if self.preloaders.len() > MAX_PRELOADERS as usize {
|
|
||||||
bail!("Preloaders exceed the limit of {MAX_PRELOADERS}");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, p) in self.fetchers.iter_mut().enumerate() {
|
|
||||||
p.idx = i as u8;
|
|
||||||
}
|
|
||||||
for (i, p) in self.preloaders.iter_mut().enumerate() {
|
|
||||||
p.idx = i as u8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
fetchers: self.fetchers,
|
fetchers: fetchers.into(),
|
||||||
spotters: self.spotters,
|
spotters: spotters.into(),
|
||||||
preloaders: self.preloaders,
|
preloaders: preloaders.into(),
|
||||||
previewers: self.previewers,
|
previewers: previewers.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,35 @@
|
||||||
use serde::Deserialize;
|
use std::ops::Deref;
|
||||||
use yazi_fs::File;
|
|
||||||
use yazi_shared::event::Action;
|
|
||||||
|
|
||||||
use crate::{Pattern, Priority};
|
use serde::Deserialize;
|
||||||
|
use yazi_shared::{Id, event::Action};
|
||||||
|
|
||||||
|
use crate::{Mixable, Pattern, Priority, Selectable, Selector, plugin::preloader_id};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Preloader {
|
pub struct Preloader {
|
||||||
|
#[serde(skip, default = "preloader_id")]
|
||||||
|
pub id: Id,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub idx: u8,
|
pub idx: u8,
|
||||||
|
#[serde(flatten)]
|
||||||
pub url: Option<Pattern>,
|
pub selector: Selector,
|
||||||
pub mime: Option<Pattern>,
|
pub run: Action,
|
||||||
pub run: Action,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub next: bool,
|
pub next: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub prio: Priority,
|
pub prio: Priority,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Preloader {
|
impl Deref for Preloader {
|
||||||
#[inline]
|
type Target = Action;
|
||||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
|
||||||
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
fn deref(&self) -> &Self::Target { &self.run }
|
||||||
|| self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Selectable for Preloader {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixable for Preloader {}
|
||||||
|
|
|
||||||
99
yazi-config/src/plugin/preloaders.rs
Normal file
99
yazi-config/src/plugin/preloaders.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
use std::{borrow::Cow, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use yazi_fs::File;
|
||||||
|
use yazi_shared::Id;
|
||||||
|
use yazi_shim::arc_swap::IntoPointee;
|
||||||
|
|
||||||
|
use super::Preloader;
|
||||||
|
use crate::Selectable;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct Preloaders(ArcSwap<Vec<Arc<Preloader>>>);
|
||||||
|
|
||||||
|
impl Deref for Preloaders {
|
||||||
|
type Target = ArcSwap<Vec<Arc<Preloader>>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Arc<Preloader>>> for Preloaders {
|
||||||
|
fn from(inner: Vec<Arc<Preloader>>) -> Self { Self(inner.into_pointee()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<Vec<Arc<Preloader>>>> for Preloaders {
|
||||||
|
fn from(inner: Arc<Vec<Arc<Preloader>>>) -> Self { Self(inner.into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Preloaders {
|
||||||
|
pub fn matches<'a>(&self, file: &'a File, mime: &'a str) -> PreloaderMatcher<'a> {
|
||||||
|
self.matcher(Some(file), Some(mime))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matcher<'a, F, M>(&self, file: Option<F>, mime: Option<M>) -> PreloaderMatcher<'a>
|
||||||
|
where
|
||||||
|
F: Into<Cow<'a, File>>,
|
||||||
|
M: Into<Cow<'a, str>>,
|
||||||
|
{
|
||||||
|
PreloaderMatcher {
|
||||||
|
preloaders: self.load_full(),
|
||||||
|
file: file.map(Into::into),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unwrap_unchecked(self) -> Vec<Arc<Preloader>> {
|
||||||
|
Arc::try_unwrap(self.0.into_inner()).expect("unique preloaders arc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PreloaderMatcher<'a> {
|
||||||
|
pub preloaders: Arc<Vec<Arc<Preloader>>>,
|
||||||
|
pub id: Id,
|
||||||
|
pub file: Option<Cow<'a, File>>,
|
||||||
|
pub mime: Option<Cow<'a, str>>,
|
||||||
|
pub all: bool,
|
||||||
|
pub offset: usize,
|
||||||
|
pub stop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Preloaders> for PreloaderMatcher<'_> {
|
||||||
|
fn from(preloaders: &Preloaders) -> Self {
|
||||||
|
Self { preloaders: preloaders.load_full(), all: true, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreloaderMatcher<'_> {
|
||||||
|
pub fn matches(&self, preloader: &Preloader) -> bool {
|
||||||
|
if self.all {
|
||||||
|
true
|
||||||
|
} else if self.id != Id::ZERO {
|
||||||
|
preloader.id == self.id
|
||||||
|
} else {
|
||||||
|
preloader.match_with(self.file.as_deref(), self.mime.as_deref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PreloaderMatcher<'_> {
|
||||||
|
type Item = Arc<Preloader>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.stop && !self.all {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(preloader) = self.preloaders.get(self.offset) {
|
||||||
|
self.offset += 1;
|
||||||
|
if self.matches(preloader) {
|
||||||
|
self.stop = !preloader.next;
|
||||||
|
return Some(preloader.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,34 @@
|
||||||
use serde::Deserialize;
|
use std::ops::Deref;
|
||||||
use yazi_fs::File;
|
|
||||||
use yazi_shared::event::Action;
|
|
||||||
|
|
||||||
use crate::Pattern;
|
use serde::Deserialize;
|
||||||
|
use yazi_shared::{Id, event::Action};
|
||||||
|
|
||||||
|
use crate::{Mixable, Pattern, Selectable, Selector, plugin::previewer_id};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Previewer {
|
pub struct Previewer {
|
||||||
pub url: Option<Pattern>,
|
#[serde(skip, default = "previewer_id")]
|
||||||
pub mime: Option<Pattern>,
|
pub id: Id,
|
||||||
pub run: Action,
|
#[serde(flatten)]
|
||||||
|
pub selector: Selector,
|
||||||
|
pub run: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Previewer {
|
impl Deref for Previewer {
|
||||||
#[inline]
|
type Target = Action;
|
||||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
|
||||||
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
|
||||||
|| self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn any_file(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_file()) }
|
fn deref(&self) -> &Self::Target { &self.run }
|
||||||
|
}
|
||||||
#[inline]
|
|
||||||
pub fn any_dir(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_dir()) }
|
impl Selectable for Previewer {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixable for Previewer {
|
||||||
|
fn any_file(&self) -> bool { self.selector.any_file() }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { self.selector.any_dir() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
121
yazi-config/src/plugin/previewers.rs
Normal file
121
yazi-config/src/plugin/previewers.rs
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
use std::{borrow::Cow, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use yazi_fs::File;
|
||||||
|
use yazi_shared::Id;
|
||||||
|
use yazi_shim::{arc_swap::IntoPointee, vec::{IndexAtError, VecExt}};
|
||||||
|
|
||||||
|
use super::Previewer;
|
||||||
|
use crate::{Selectable, mix};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct Previewers(ArcSwap<Vec<Arc<Previewer>>>);
|
||||||
|
|
||||||
|
impl Deref for Previewers {
|
||||||
|
type Target = ArcSwap<Vec<Arc<Previewer>>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Arc<Previewer>>> for Previewers {
|
||||||
|
fn from(inner: Vec<Arc<Previewer>>) -> Self { Self(inner.into_pointee()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Previewers {
|
||||||
|
pub fn matches(&self, file: &File, mime: &str) -> Option<Arc<Previewer>> {
|
||||||
|
self.matcher(Some(file), Some(mime)).next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matcher<'a, F, M>(&self, file: Option<F>, mime: Option<M>) -> PreviewerMatcher<'a>
|
||||||
|
where
|
||||||
|
F: Into<Cow<'a, File>>,
|
||||||
|
M: Into<Cow<'a, str>>,
|
||||||
|
{
|
||||||
|
PreviewerMatcher {
|
||||||
|
previewers: self.0.load_full(),
|
||||||
|
file: file.map(Into::into),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&self, index: isize, previewer: Arc<Previewer>) -> Result<(), IndexAtError> {
|
||||||
|
let mut err = None;
|
||||||
|
|
||||||
|
self.0.rcu(|previewers| match previewers.index_at(index) {
|
||||||
|
Ok(n) if n == previewers.len() => {
|
||||||
|
mix(Vec::<Previewer>::new(), previewers.iter().cloned(), [previewer.clone()])
|
||||||
|
}
|
||||||
|
Ok(n) => {
|
||||||
|
let (before, after) = previewers.split_at(n);
|
||||||
|
mix(
|
||||||
|
Vec::<Previewer>::new(),
|
||||||
|
before.iter().cloned().chain([previewer.clone()]).chain(after.iter().cloned()),
|
||||||
|
Vec::<Previewer>::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
err = Some(e);
|
||||||
|
Vec::clone(previewers)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
err.map_or(Ok(()), Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self, matcher: PreviewerMatcher) {
|
||||||
|
self.0.rcu(|previewers| {
|
||||||
|
let mut next = Vec::clone(previewers);
|
||||||
|
next.retain(|previewer| !matcher.matches(previewer));
|
||||||
|
next
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unwrap_unchecked(self) -> Vec<Arc<Previewer>> {
|
||||||
|
Arc::try_unwrap(self.0.into_inner()).expect("unique previewers arc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PreviewerMatcher<'a> {
|
||||||
|
pub previewers: Arc<Vec<Arc<Previewer>>>,
|
||||||
|
pub id: Id,
|
||||||
|
pub file: Option<Cow<'a, File>>,
|
||||||
|
pub mime: Option<Cow<'a, str>>,
|
||||||
|
pub all: bool,
|
||||||
|
pub offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Previewers> for PreviewerMatcher<'_> {
|
||||||
|
fn from(previewers: &Previewers) -> Self {
|
||||||
|
Self { previewers: previewers.load_full(), all: true, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreviewerMatcher<'_> {
|
||||||
|
pub fn matches(&self, previewer: &Previewer) -> bool {
|
||||||
|
if self.all {
|
||||||
|
true
|
||||||
|
} else if self.id != Id::ZERO {
|
||||||
|
previewer.id == self.id
|
||||||
|
} else {
|
||||||
|
previewer.match_with(self.file.as_deref(), self.mime.as_deref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PreviewerMatcher<'_> {
|
||||||
|
type Item = Arc<Previewer>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(previewer) = self.previewers.get(self.offset) {
|
||||||
|
self.offset += 1;
|
||||||
|
if self.matches(previewer) {
|
||||||
|
return Some(previewer.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,34 @@
|
||||||
use serde::Deserialize;
|
use std::ops::Deref;
|
||||||
use yazi_fs::File;
|
|
||||||
use yazi_shared::event::Action;
|
|
||||||
|
|
||||||
use crate::Pattern;
|
use serde::Deserialize;
|
||||||
|
use yazi_shared::{Id, event::Action};
|
||||||
|
|
||||||
|
use crate::{Mixable, Pattern, Selectable, Selector, plugin::spotter_id};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Spotter {
|
pub struct Spotter {
|
||||||
pub url: Option<Pattern>,
|
#[serde(skip, default = "spotter_id")]
|
||||||
pub mime: Option<Pattern>,
|
pub id: Id,
|
||||||
pub run: Action,
|
#[serde(flatten)]
|
||||||
|
pub selector: Selector,
|
||||||
|
pub run: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spotter {
|
impl Deref for Spotter {
|
||||||
#[inline]
|
type Target = Action;
|
||||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
|
||||||
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
|
||||||
|| self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn any_file(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_file()) }
|
fn deref(&self) -> &Self::Target { &self.run }
|
||||||
|
}
|
||||||
#[inline]
|
|
||||||
pub fn any_dir(&self) -> bool { self.url.as_ref().is_some_and(|p| p.any_dir()) }
|
impl Selectable for Spotter {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixable for Spotter {
|
||||||
|
fn any_file(&self) -> bool { self.selector.any_file() }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { self.selector.any_dir() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
90
yazi-config/src/plugin/spotters.rs
Normal file
90
yazi-config/src/plugin/spotters.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use std::{borrow::Cow, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use yazi_fs::File;
|
||||||
|
use yazi_shared::Id;
|
||||||
|
use yazi_shim::arc_swap::IntoPointee;
|
||||||
|
|
||||||
|
use super::Spotter;
|
||||||
|
use crate::Selectable;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct Spotters(ArcSwap<Vec<Arc<Spotter>>>);
|
||||||
|
|
||||||
|
impl Deref for Spotters {
|
||||||
|
type Target = ArcSwap<Vec<Arc<Spotter>>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Arc<Spotter>>> for Spotters {
|
||||||
|
fn from(inner: Vec<Arc<Spotter>>) -> Self { Self(inner.into_pointee()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spotters {
|
||||||
|
pub fn matches(&self, file: &File, mime: &str) -> Option<Arc<Spotter>> {
|
||||||
|
self.matcher(Some(file), Some(mime)).next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matcher<'a, F, M>(&self, file: Option<F>, mime: Option<M>) -> SpotterMatcher<'a>
|
||||||
|
where
|
||||||
|
F: Into<Cow<'a, File>>,
|
||||||
|
M: Into<Cow<'a, str>>,
|
||||||
|
{
|
||||||
|
SpotterMatcher {
|
||||||
|
spotters: self.load_full(),
|
||||||
|
id: Id::ZERO,
|
||||||
|
file: file.map(Into::into),
|
||||||
|
mime: mime.map(Into::into),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unwrap_unchecked(self) -> Vec<Arc<Spotter>> {
|
||||||
|
Arc::try_unwrap(self.0.into_inner()).expect("unique spotters arc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Matcher
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SpotterMatcher<'a> {
|
||||||
|
pub spotters: Arc<Vec<Arc<Spotter>>>,
|
||||||
|
pub id: Id,
|
||||||
|
pub file: Option<Cow<'a, File>>,
|
||||||
|
pub mime: Option<Cow<'a, str>>,
|
||||||
|
pub all: bool,
|
||||||
|
pub offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Spotters> for SpotterMatcher<'_> {
|
||||||
|
fn from(spotters: &Spotters) -> Self {
|
||||||
|
Self { spotters: spotters.load_full(), all: true, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpotterMatcher<'_> {
|
||||||
|
pub fn matches(&self, spotter: &Spotter) -> bool {
|
||||||
|
if self.all {
|
||||||
|
true
|
||||||
|
} else if self.id != Id::ZERO {
|
||||||
|
spotter.id == self.id
|
||||||
|
} else {
|
||||||
|
spotter.match_with(self.file.as_deref(), self.mime.as_deref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for SpotterMatcher<'_> {
|
||||||
|
type Item = Arc<Spotter>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(spotter) = self.spotters.get(self.offset) {
|
||||||
|
self.offset += 1;
|
||||||
|
if self.matches(spotter) {
|
||||||
|
return Some(spotter.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
|
|
||||||
use super::{Offset, Origin};
|
use super::{Offset, Origin};
|
||||||
use crate::popup::Position;
|
use crate::popup::Position;
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Confirm {
|
pub struct Confirm {
|
||||||
// trash
|
// trash
|
||||||
pub trash_title: String,
|
pub trash_title: String,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
|
|
||||||
use super::{Offset, Origin};
|
use super::{Offset, Origin};
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
pub cursor_blink: bool,
|
pub cursor_blink: bool,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
|
|
||||||
use super::{Offset, Origin};
|
use super::{Offset, Origin};
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Pick {
|
pub struct Pick {
|
||||||
// open
|
// open
|
||||||
pub open_title: String,
|
pub open_title: String,
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,4 @@ impl Preset {
|
||||||
pub(super) fn vfs() -> Result<Vfs, toml::de::Error> {
|
pub(super) fn vfs() -> Result<Vfs, toml::de::Error> {
|
||||||
toml::from_str(&yazi_macro::config_preset!("vfs"))
|
toml::from_str(&yazi_macro::config_preset!("vfs"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn mix<E, A, B, C>(a: A, b: B, c: C) -> impl Iterator<Item = E>
|
|
||||||
where
|
|
||||||
A: IntoIterator<Item = E>,
|
|
||||||
B: IntoIterator<Item = E>,
|
|
||||||
C: IntoIterator<Item = E>,
|
|
||||||
{
|
|
||||||
a.into_iter().chain(b).chain(c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
use yazi_fs::Xdg;
|
use yazi_fs::Xdg;
|
||||||
use yazi_shared::{SStr, timestamp_us};
|
use yazi_shared::{SStr, timestamp_us};
|
||||||
|
use yazi_shim::toml::DeserializeOverHook;
|
||||||
|
|
||||||
use super::PreviewWrap;
|
use super::PreviewWrap;
|
||||||
use crate::normalize_path;
|
use crate::normalize_path;
|
||||||
|
|
@ -16,10 +17,13 @@ pub struct Preview {
|
||||||
pub max_width: u16,
|
pub max_width: u16,
|
||||||
pub max_height: u16,
|
pub max_height: u16,
|
||||||
|
|
||||||
|
#[serde(deserialize_with = "deserialize_cache_dir")]
|
||||||
pub cache_dir: PathBuf,
|
pub cache_dir: PathBuf,
|
||||||
|
|
||||||
|
#[serde(deserialize_with = "deserialize_image_delay")]
|
||||||
pub image_delay: u8,
|
pub image_delay: u8,
|
||||||
pub image_filter: String,
|
pub image_filter: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_image_quality")]
|
||||||
pub image_quality: u8,
|
pub image_quality: u8,
|
||||||
|
|
||||||
pub ueberzug_scale: f32,
|
pub ueberzug_scale: f32,
|
||||||
|
|
@ -43,25 +47,50 @@ impl Preview {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Preview {
|
impl DeserializeOverHook for Preview {
|
||||||
pub(crate) fn reshape(mut self) -> Result<Self> {
|
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||||
if self.image_delay > 100 {
|
|
||||||
bail!("[preview].image_delay must be between 0 and 100.");
|
|
||||||
} else if self.image_quality < 50 || self.image_quality > 90 {
|
|
||||||
bail!("[preview].image_quality must be between 50 and 90.");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cache_dir = if self.cache_dir.as_os_str().is_empty() {
|
|
||||||
Xdg::cache_dir().to_owned()
|
|
||||||
} else if let Some(p) = normalize_path(self.cache_dir) {
|
|
||||||
p
|
|
||||||
} else {
|
|
||||||
bail!("[preview].cache_dir must be either empty or an absolute path.");
|
|
||||||
};
|
|
||||||
|
|
||||||
std::fs::create_dir_all(&self.cache_dir)
|
std::fs::create_dir_all(&self.cache_dir)
|
||||||
.context(format!("Failed to create cache directory: {}", self.cache_dir.display()))?;
|
.context(format!("Failed to create cache directory: {}", self.cache_dir.display()))
|
||||||
|
.map_err(serde::de::Error::custom)?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_cache_dir<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let path = PathBuf::deserialize(deserializer)?;
|
||||||
|
if path.as_os_str().is_empty() {
|
||||||
|
Ok(Xdg::cache_dir().to_owned())
|
||||||
|
} else {
|
||||||
|
normalize_path(path).ok_or_else(|| {
|
||||||
|
serde::de::Error::custom("cache_dir must be either empty or an absolute path.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_image_delay<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = u8::deserialize(deserializer)?;
|
||||||
|
if value <= 100 {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom("image_delay must be between 0 and 100."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_image_quality<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = u8::deserialize(deserializer)?;
|
||||||
|
if (50..=90).contains(&value) {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom("image_quality must be between 50 and 90."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
21
yazi-config/src/selectable.rs
Normal file
21
yazi-config/src/selectable.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
use yazi_fs::File;
|
||||||
|
|
||||||
|
use crate::Pattern;
|
||||||
|
|
||||||
|
pub trait Selectable {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern>;
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn matches(&self, file: &File, mime: &str) -> bool { self.match_with(Some(file), Some(mime)) }
|
||||||
|
|
||||||
|
fn match_with(&self, file: Option<&File>, mime: Option<&str>) -> bool {
|
||||||
|
match (self.url_pat(), self.mime_pat(), file, mime) {
|
||||||
|
(_, Some(mp), _, Some(m)) if !mp.match_mime(m) => false,
|
||||||
|
(Some(up), _, Some(f), _) if !up.match_url(&f.url, f.is_dir()) => false,
|
||||||
|
(_, Some(_), _, None) | (Some(_), _, None, _) => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
yazi-config/src/selector.rs
Normal file
42
yazi-config/src/selector.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use anyhow::{Result, ensure};
|
||||||
|
use serde::{Deserialize, Deserializer, de};
|
||||||
|
|
||||||
|
use crate::{Mixable, Pattern, Selectable};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Selector {
|
||||||
|
url: Option<Pattern>,
|
||||||
|
mime: Option<Pattern>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Selector {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Shadow {
|
||||||
|
url: Option<Pattern>,
|
||||||
|
mime: Option<Pattern>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let shadow = Shadow::deserialize(deserializer)?;
|
||||||
|
Self::new(shadow.url, shadow.mime).map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Selector {
|
||||||
|
pub fn new(url: Option<Pattern>, mime: Option<Pattern>) -> Result<Self> {
|
||||||
|
ensure!(url.is_some() || mime.is_some(), "at least one of `url` or `mime` must be specified");
|
||||||
|
Ok(Self { url, mime })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Selectable for Selector {
|
||||||
|
fn url_pat(&self) -> Option<&Pattern> { self.url.as_ref() }
|
||||||
|
|
||||||
|
fn mime_pat(&self) -> Option<&Pattern> { self.mime.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mixable for Selector {
|
||||||
|
fn any_file(&self) -> bool { self.url_pat().is_some_and(|p| p.any_file()) }
|
||||||
|
|
||||||
|
fn any_dir(&self) -> bool { self.url_pat().is_some_and(|p| p.any_dir()) }
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,20 @@
|
||||||
use anyhow::{Result, bail};
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, DeserializeOver2)]
|
#[derive(Debug, Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Tasks {
|
pub struct Tasks {
|
||||||
pub file_workers: u8,
|
pub file_workers: NonZeroU8,
|
||||||
pub plugin_workers: u8,
|
pub plugin_workers: NonZeroU8,
|
||||||
pub fetch_workers: u8,
|
pub fetch_workers: NonZeroU8,
|
||||||
pub preload_workers: u8,
|
pub preload_workers: NonZeroU8,
|
||||||
pub process_workers: u8,
|
pub process_workers: NonZeroU8,
|
||||||
|
|
||||||
pub bizarre_retry: u8,
|
pub bizarre_retry: NonZeroU8,
|
||||||
|
|
||||||
pub image_alloc: u32,
|
pub image_alloc: u32,
|
||||||
pub image_bound: [u16; 2],
|
pub image_bound: [u16; 2],
|
||||||
|
|
||||||
pub suppress_preload: bool,
|
pub suppress_preload: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tasks {
|
|
||||||
pub(crate) fn reshape(self) -> Result<Self> {
|
|
||||||
if self.file_workers < 1 {
|
|
||||||
bail!("[tasks].file_workers must be at least 1.");
|
|
||||||
} else if self.plugin_workers < 1 {
|
|
||||||
bail!("[tasks].plugin_workers must be at least 1.");
|
|
||||||
} else if self.fetch_workers < 1 {
|
|
||||||
bail!("[tasks].fetch_workers must be at least 1.");
|
|
||||||
} else if self.preload_workers < 1 {
|
|
||||||
bail!("[tasks].preload_workers must be at least 1.");
|
|
||||||
} else if self.process_workers < 1 {
|
|
||||||
bail!("[tasks].process_workers must be at least 1.");
|
|
||||||
} else if self.bizarre_retry < 1 {
|
|
||||||
bail!("[tasks].bizarre_retry must be at least 1.");
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
|
|
||||||
use super::Is;
|
use super::Is;
|
||||||
use crate::{Pattern, Style};
|
use crate::{Pattern, Selectable, Selector, Style};
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Filetype {
|
pub struct Filetype {
|
||||||
rules: Vec<FiletypeRule>,
|
rules: Vec<FiletypeRule>,
|
||||||
}
|
}
|
||||||
|
|
@ -22,19 +22,22 @@ impl Deref for Filetype {
|
||||||
pub struct FiletypeRule {
|
pub struct FiletypeRule {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
is: Is,
|
is: Is,
|
||||||
url: Option<Pattern>,
|
#[serde(flatten)]
|
||||||
mime: Option<Pattern>,
|
selector: Selector,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub style: Style,
|
pub style: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FiletypeRule {
|
impl Selectable for FiletypeRule {
|
||||||
pub fn matches(&self, file: &File, mime: &str) -> bool {
|
fn url_pat(&self) -> Option<&Pattern> { self.selector.url_pat() }
|
||||||
if !self.is.check(&file.cha) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|
fn mime_pat(&self) -> Option<&Pattern> { self.selector.mime_pat() }
|
||||||
|| self.url.as_ref().is_some_and(|n| n.match_url(&file.url, file.is_dir()))
|
|
||||||
|
fn match_with(&self, file: Option<&File>, mime: Option<&str>) -> bool {
|
||||||
|
match (self.is.enabled(), file) {
|
||||||
|
(Some(is), Some(f)) if !is.check(&f.cha) => false,
|
||||||
|
(Some(_), None) => false,
|
||||||
|
_ => self.selector.match_with(file, mime),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,34 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::{Deserialize, Serialize, de::IntoDeserializer};
|
use arc_swap::ArcSwap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use toml::{Spanned, de::DeTable};
|
use toml::{Spanned, de::DeTable};
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
||||||
use yazi_fs::Xdg;
|
use yazi_fs::Xdg;
|
||||||
|
use yazi_shim::toml::deserialize_spanned;
|
||||||
|
|
||||||
use crate::error_with_input;
|
use crate::error_with_input;
|
||||||
|
|
||||||
#[derive(Default, Deserialize, DeserializeOver2, Serialize)]
|
#[derive(Default, Deserialize, DeserializeOver, DeserializeOver2, Serialize)]
|
||||||
pub struct Flavor {
|
pub struct Flavor {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dark: String,
|
pub dark: ArcSwap<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub light: String,
|
pub light: ArcSwap<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flavor {
|
impl Flavor {
|
||||||
pub(crate) fn from_theme(theme: &Spanned<DeTable>, input: &str) -> Result<Self, toml::de::Error> {
|
pub(crate) fn from_theme(theme: &Spanned<DeTable>, input: &str) -> Result<Self, toml::de::Error> {
|
||||||
if let Some(value) = theme.get_ref().get("flavor").cloned() {
|
if let Some(value) = theme.get_ref().get("flavor").cloned() {
|
||||||
error_with_input(Self::deserialize(value.into_deserializer()), input)
|
error_with_input(deserialize_spanned(value), input)
|
||||||
} else {
|
} else {
|
||||||
Ok(Self::default())
|
Ok(Self::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read(&self, light: bool) -> Result<String> {
|
pub(crate) fn read(&self, light: bool) -> Result<String> {
|
||||||
Ok(match if light { self.light.as_str() } else { self.dark.as_str() } {
|
Ok(match if light { self.light.load() } else { self.dark.load() }.as_str() {
|
||||||
"" => String::new(),
|
"" => String::new(),
|
||||||
name => {
|
name => {
|
||||||
let p = Xdg::config_dir().join(format!("flavors/{name}.yazi/flavor.toml"));
|
let p = Xdg::config_dir().join(format!("flavors/{name}.yazi/flavor.toml"));
|
||||||
|
|
@ -36,7 +38,7 @@ impl Flavor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn syntect_path(&self, light: bool) -> Option<PathBuf> {
|
pub(crate) fn syntect_path(&self, light: bool) -> Option<PathBuf> {
|
||||||
match if light { self.light.as_str() } else { self.dark.as_str() } {
|
match if light { self.light.load() } else { self.dark.load() }.as_str() {
|
||||||
"" => None,
|
"" => None,
|
||||||
name => Some(Xdg::config_dir().join(format!("flavors/{name}.yazi/tmtheme.xml"))),
|
name => Some(Xdg::config_dir().join(format!("flavors/{name}.yazi/tmtheme.xml"))),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,47 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use ratatui::style::Color;
|
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::{Condition, url::UrlLike};
|
use yazi_shared::{Condition, url::UrlLike};
|
||||||
|
use yazi_shim::toml::DeserializeOverHook;
|
||||||
|
|
||||||
use crate::{Icon as I, Pattern, Style};
|
use crate::{Icon as I, Mixable, Pattern, mix};
|
||||||
|
|
||||||
#[derive(Default, Deserialize, DeserializeOver2)]
|
#[derive(Default, Deserialize, DeserializeOver2)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
globs: PatIcons,
|
globs: Vec<IconPat>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_globs: PatIcons,
|
prepend_globs: Vec<IconPat>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_globs: PatIcons,
|
append_globs: Vec<IconPat>,
|
||||||
|
|
||||||
dirs: StrIcons,
|
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
dirs: HashMap<String, I>,
|
||||||
prepend_dirs: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
prepend_dirs: HashMap<String, I>,
|
||||||
append_dirs: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
|
append_dirs: HashMap<String, I>,
|
||||||
|
|
||||||
files: StrIcons,
|
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
files: HashMap<String, I>,
|
||||||
prepend_files: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
prepend_files: HashMap<String, I>,
|
||||||
append_files: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
|
append_files: HashMap<String, I>,
|
||||||
|
|
||||||
exts: StrIcons,
|
#[serde(deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
exts: HashMap<String, I>,
|
||||||
prepend_exts: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
#[serde(default)]
|
prepend_exts: HashMap<String, I>,
|
||||||
append_exts: StrIcons,
|
#[serde(default, deserialize_with = "deserialize_named_icons")]
|
||||||
|
append_exts: HashMap<String, I>,
|
||||||
|
|
||||||
conds: CondIcons,
|
conds: Vec<IconCond>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
prepend_conds: CondIcons,
|
prepend_conds: Vec<IconCond>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
append_conds: CondIcons,
|
append_conds: Vec<IconCond>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
|
|
@ -68,11 +69,11 @@ impl Icon {
|
||||||
"hovered" => hovered,
|
"hovered" => hovered,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
self.conds.iter().find(|&c| c.r#if.eval(f) == Some(true)).map(|c| &c.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_by_glob(&self, file: &File) -> Option<&I> {
|
fn match_by_glob(&self, file: &File) -> Option<&I> {
|
||||||
self.globs.iter().find(|(p, _)| p.match_url(&file.url, file.is_dir())).map(|(_, i)| i)
|
self.globs.iter().find(|&g| g.url.match_url(&file.url, file.is_dir())).map(|g| &g.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_by_name(&self, file: &File) -> Option<&I> {
|
fn match_by_name(&self, file: &File) -> Option<&I> {
|
||||||
|
|
@ -94,115 +95,59 @@ impl Icon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl DeserializeOverHook for Icon {
|
||||||
pub(crate) fn reshape(self) -> Result<Self> {
|
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
globs: PatIcons(
|
globs: mix(self.prepend_globs, self.globs, self.append_globs),
|
||||||
self.prepend_globs.0.into_iter().chain(self.globs.0).chain(self.append_globs.0).collect(),
|
dirs: self.append_dirs.into_iter().chain(self.dirs).chain(self.prepend_dirs).collect(),
|
||||||
),
|
files: self.append_files.into_iter().chain(self.files).chain(self.prepend_files).collect(),
|
||||||
dirs: StrIcons(
|
exts: self.append_exts.into_iter().chain(self.exts).chain(self.prepend_exts).collect(),
|
||||||
self.append_dirs.0.into_iter().chain(self.dirs.0).chain(self.prepend_dirs.0).collect(),
|
conds: mix(self.prepend_conds, self.conds, self.append_conds),
|
||||||
),
|
|
||||||
files: StrIcons(
|
|
||||||
self.append_files.0.into_iter().chain(self.files.0).chain(self.prepend_files.0).collect(),
|
|
||||||
),
|
|
||||||
exts: StrIcons(
|
|
||||||
self.append_exts.0.into_iter().chain(self.exts.0).chain(self.prepend_exts.0).collect(),
|
|
||||||
),
|
|
||||||
conds: CondIcons(
|
|
||||||
self.prepend_conds.0.into_iter().chain(self.conds.0).chain(self.append_conds.0).collect(),
|
|
||||||
),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
// --- IconPat
|
||||||
pub struct PatIcons(Vec<(Pattern, I)>);
|
#[derive(Deserialize)]
|
||||||
|
pub struct IconPat {
|
||||||
impl Deref for PatIcons {
|
pub url: Pattern,
|
||||||
type Target = Vec<(Pattern, I)>;
|
#[serde(flatten)]
|
||||||
|
pub icon: I,
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for PatIcons {
|
impl Mixable for IconPat {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn any_file(&self) -> bool { self.url.any_file() }
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Shadow {
|
|
||||||
url: Pattern,
|
|
||||||
text: String,
|
|
||||||
fg: Option<Color>,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self(
|
fn any_dir(&self) -> bool { self.url.any_dir() }
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| (s.url, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
// --- IconNamed
|
||||||
pub struct StrIcons(HashMap<String, I>);
|
#[derive(Deserialize)]
|
||||||
|
struct IconNamed {
|
||||||
impl Deref for StrIcons {
|
name: String,
|
||||||
type Target = HashMap<String, I>;
|
#[serde(flatten)]
|
||||||
|
icon: I,
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for StrIcons {
|
fn deserialize_named_icons<'de, D>(deserializer: D) -> Result<HashMap<String, I>, D::Error>
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
where
|
||||||
where
|
D: Deserializer<'de>,
|
||||||
D: Deserializer<'de>,
|
{
|
||||||
{
|
Ok(
|
||||||
#[derive(Deserialize)]
|
Vec::<IconNamed>::deserialize(deserializer)?
|
||||||
struct Shadow {
|
.into_iter()
|
||||||
name: String,
|
.map(|entry| (entry.name, entry.icon))
|
||||||
text: String,
|
.collect(),
|
||||||
fg: Option<Color>,
|
)
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self(
|
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| (s.name, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
// --- IconCond
|
||||||
pub struct CondIcons(Vec<(Condition, I)>);
|
#[derive(Deserialize)]
|
||||||
|
pub struct IconCond {
|
||||||
impl Deref for CondIcons {
|
pub r#if: Condition,
|
||||||
type Target = Vec<(Condition, I)>;
|
#[serde(flatten)]
|
||||||
|
pub icon: I,
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for CondIcons {
|
impl Mixable for IconCond {}
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Shadow {
|
|
||||||
r#if: Condition,
|
|
||||||
text: String,
|
|
||||||
fg: Option<Color>,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self(
|
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| (s.r#if, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_fs::cha::Cha;
|
use yazi_fs::cha::Cha;
|
||||||
|
|
||||||
#[derive(Default, Deserialize)]
|
#[derive(Clone, Copy, Default, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Is {
|
pub enum Is {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -19,7 +19,10 @@ pub enum Is {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Is {
|
impl Is {
|
||||||
pub fn check(&self, cha: &Cha) -> bool {
|
#[inline]
|
||||||
|
pub fn enabled(self) -> Option<Self> { (!matches!(self, Self::None)).then_some(self) }
|
||||||
|
|
||||||
|
pub fn check(self, cha: &Cha) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::None => true,
|
Self::None => true,
|
||||||
Self::Hidden => cha.is_hidden(),
|
Self::Hidden => cha.is_hidden(),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result};
|
||||||
use serde::Deserialize;
|
use arc_swap::ArcSwap;
|
||||||
|
use serde::{Deserialize, Deserializer, de};
|
||||||
use yazi_codegen::{DeserializeOver, DeserializeOver1, DeserializeOver2};
|
use yazi_codegen::{DeserializeOver, DeserializeOver1, DeserializeOver2};
|
||||||
use yazi_fs::{Xdg, ok_or_not_found};
|
use yazi_fs::{Xdg, ok_or_not_found};
|
||||||
|
use yazi_shared::SyncCell;
|
||||||
|
use yazi_shim::arc_swap::IntoPointee;
|
||||||
|
|
||||||
use super::{Filetype, Flavor, Icon};
|
use super::{Filetype, Flavor, Icon};
|
||||||
use crate::{Style, normalize_path};
|
use crate::{Style, normalize_path};
|
||||||
|
|
@ -28,199 +31,199 @@ pub struct Theme {
|
||||||
pub help: Help,
|
pub help: Help,
|
||||||
|
|
||||||
// File-specific styles
|
// File-specific styles
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub filetype: Filetype,
|
pub filetype: Filetype,
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub icon: Icon,
|
pub icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub overall: Style,
|
pub overall: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Mgr {
|
pub struct Mgr {
|
||||||
pub cwd: Style,
|
pub cwd: SyncCell<Style>,
|
||||||
|
|
||||||
// Find
|
// Find
|
||||||
pub find_keyword: Style,
|
pub find_keyword: SyncCell<Style>,
|
||||||
pub find_position: Style,
|
pub find_position: SyncCell<Style>,
|
||||||
|
|
||||||
// Symlink
|
// Symlink
|
||||||
pub symlink_target: Style,
|
pub symlink_target: SyncCell<Style>,
|
||||||
|
|
||||||
// Marker
|
// Marker
|
||||||
pub marker_copied: Style,
|
pub marker_copied: SyncCell<Style>,
|
||||||
pub marker_cut: Style,
|
pub marker_cut: SyncCell<Style>,
|
||||||
pub marker_marked: Style,
|
pub marker_marked: SyncCell<Style>,
|
||||||
pub marker_selected: Style,
|
pub marker_selected: SyncCell<Style>,
|
||||||
pub marker_symbol: String,
|
pub marker_symbol: ArcSwap<String>,
|
||||||
|
|
||||||
// Count
|
// Count
|
||||||
pub count_copied: Style,
|
pub count_copied: SyncCell<Style>,
|
||||||
pub count_cut: Style,
|
pub count_cut: SyncCell<Style>,
|
||||||
pub count_selected: Style,
|
pub count_selected: SyncCell<Style>,
|
||||||
|
|
||||||
// Border
|
// Border
|
||||||
pub border_symbol: String,
|
pub border_symbol: ArcSwap<String>,
|
||||||
pub border_style: Style,
|
pub border_style: SyncCell<Style>,
|
||||||
|
|
||||||
// Highlighting
|
// Highlighting
|
||||||
pub syntect_theme: PathBuf,
|
#[serde(deserialize_with = "deserialize_syntect_theme")]
|
||||||
|
pub syntect_theme: ArcSwap<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Tabs {
|
pub struct Tabs {
|
||||||
pub active: Style,
|
pub active: SyncCell<Style>,
|
||||||
pub inactive: Style,
|
pub inactive: SyncCell<Style>,
|
||||||
|
|
||||||
pub sep_inner: TabsSep,
|
pub sep_inner: ArcSwap<TabsSep>,
|
||||||
pub sep_outer: TabsSep,
|
pub sep_outer: ArcSwap<TabsSep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize)]
|
||||||
pub struct TabsSep {
|
pub struct TabsSep {
|
||||||
pub open: String,
|
pub open: String,
|
||||||
pub close: String,
|
pub close: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Mode {
|
pub struct Mode {
|
||||||
pub normal_main: Style,
|
pub normal_main: SyncCell<Style>,
|
||||||
pub normal_alt: Style,
|
pub normal_alt: SyncCell<Style>,
|
||||||
|
|
||||||
pub select_main: Style,
|
pub select_main: SyncCell<Style>,
|
||||||
pub select_alt: Style,
|
pub select_alt: SyncCell<Style>,
|
||||||
|
|
||||||
pub unset_main: Style,
|
pub unset_main: SyncCell<Style>,
|
||||||
pub unset_alt: Style,
|
pub unset_alt: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Indicator {
|
pub struct Indicator {
|
||||||
pub parent: Style,
|
pub parent: SyncCell<Style>,
|
||||||
pub current: Style,
|
pub current: SyncCell<Style>,
|
||||||
pub preview: Style,
|
pub preview: SyncCell<Style>,
|
||||||
pub padding: IndicatorPadding,
|
pub padding: ArcSwap<IndicatorPadding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize)]
|
||||||
pub struct IndicatorPadding {
|
pub struct IndicatorPadding {
|
||||||
pub open: String,
|
pub open: String,
|
||||||
pub close: String,
|
pub close: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub overall: Style,
|
pub overall: SyncCell<Style>,
|
||||||
pub sep_left: StatusSep,
|
pub sep_left: ArcSwap<StatusSep>,
|
||||||
pub sep_right: StatusSep,
|
pub sep_right: ArcSwap<StatusSep>,
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
pub perm_sep: Style,
|
pub perm_sep: SyncCell<Style>,
|
||||||
pub perm_type: Style,
|
pub perm_type: SyncCell<Style>,
|
||||||
pub perm_read: Style,
|
pub perm_read: SyncCell<Style>,
|
||||||
pub perm_write: Style,
|
pub perm_write: SyncCell<Style>,
|
||||||
pub perm_exec: Style,
|
pub perm_exec: SyncCell<Style>,
|
||||||
|
|
||||||
// Progress
|
// Progress
|
||||||
pub progress_label: Style,
|
pub progress_label: SyncCell<Style>,
|
||||||
pub progress_normal: Style,
|
pub progress_normal: SyncCell<Style>,
|
||||||
pub progress_error: Style,
|
pub progress_error: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize)]
|
||||||
pub struct StatusSep {
|
pub struct StatusSep {
|
||||||
pub open: String,
|
pub open: String,
|
||||||
pub close: String,
|
pub close: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Which {
|
pub struct Which {
|
||||||
pub cols: u8,
|
#[serde(deserialize_with = "deserialize_which_cols")]
|
||||||
pub mask: Style,
|
pub cols: SyncCell<u8>,
|
||||||
pub cand: Style,
|
pub mask: SyncCell<Style>,
|
||||||
pub rest: Style,
|
pub cand: SyncCell<Style>,
|
||||||
pub desc: Style,
|
pub rest: SyncCell<Style>,
|
||||||
|
pub desc: SyncCell<Style>,
|
||||||
|
|
||||||
pub separator: String,
|
pub separator: ArcSwap<String>,
|
||||||
pub separator_style: Style,
|
pub separator_style: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Confirm {
|
pub struct Confirm {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub title: Style,
|
pub title: SyncCell<Style>,
|
||||||
pub body: Style,
|
pub body: SyncCell<Style>,
|
||||||
pub list: Style,
|
pub list: SyncCell<Style>,
|
||||||
|
|
||||||
pub btn_yes: Style,
|
pub btn_yes: SyncCell<Style>,
|
||||||
pub btn_no: Style,
|
pub btn_no: SyncCell<Style>,
|
||||||
pub btn_labels: [String; 2],
|
pub btn_labels: [String; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Spot {
|
pub struct Spot {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub title: Style,
|
pub title: SyncCell<Style>,
|
||||||
|
|
||||||
pub tbl_col: Style,
|
pub tbl_col: SyncCell<Style>,
|
||||||
pub tbl_cell: Style,
|
pub tbl_cell: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Notify {
|
pub struct Notify {
|
||||||
pub title_info: Style,
|
pub title_info: SyncCell<Style>,
|
||||||
pub title_warn: Style,
|
pub title_warn: SyncCell<Style>,
|
||||||
pub title_error: Style,
|
pub title_error: SyncCell<Style>,
|
||||||
|
|
||||||
pub icon_info: String,
|
pub icon_info: ArcSwap<String>,
|
||||||
pub icon_warn: String,
|
pub icon_warn: ArcSwap<String>,
|
||||||
pub icon_error: String,
|
pub icon_error: ArcSwap<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Pick {
|
pub struct Pick {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub active: Style,
|
pub active: SyncCell<Style>,
|
||||||
pub inactive: Style,
|
pub inactive: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub title: Style,
|
pub title: SyncCell<Style>,
|
||||||
pub value: Style,
|
pub value: SyncCell<Style>,
|
||||||
pub selected: Style,
|
pub selected: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Cmp {
|
pub struct Cmp {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub active: Style,
|
pub active: SyncCell<Style>,
|
||||||
pub inactive: Style,
|
pub inactive: SyncCell<Style>,
|
||||||
|
|
||||||
pub icon_file: String,
|
pub icon_file: ArcSwap<String>,
|
||||||
pub icon_folder: String,
|
pub icon_folder: ArcSwap<String>,
|
||||||
pub icon_command: String,
|
pub icon_command: ArcSwap<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Tasks {
|
pub struct Tasks {
|
||||||
pub border: Style,
|
pub border: SyncCell<Style>,
|
||||||
pub title: Style,
|
pub title: SyncCell<Style>,
|
||||||
pub hovered: Style,
|
pub hovered: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DeserializeOver2)]
|
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||||
pub struct Help {
|
pub struct Help {
|
||||||
pub on: Style,
|
pub on: SyncCell<Style>,
|
||||||
pub run: Style,
|
pub run: SyncCell<Style>,
|
||||||
pub desc: Style,
|
pub desc: SyncCell<Style>,
|
||||||
|
|
||||||
pub hovered: Style,
|
pub hovered: SyncCell<Style>,
|
||||||
pub footer: Style,
|
pub footer: SyncCell<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
|
@ -230,23 +233,44 @@ impl Theme {
|
||||||
.with_context(|| format!("Failed to read theme {p:?}"))
|
.with_context(|| format!("Failed to read theme {p:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: remove
|
||||||
pub(crate) fn reshape(mut self, light: bool) -> Result<Self> {
|
pub(crate) fn reshape(mut self, light: bool) -> Result<Self> {
|
||||||
if self.which.cols < 1 || self.which.cols > 3 {
|
if let Some(p) = self.flavor.syntect_path(light) {
|
||||||
bail!("[which].cols must be between 1 and 3");
|
self.mgr.syntect_theme = p.into_pointee();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.icon = self.icon.reshape()?;
|
|
||||||
|
|
||||||
self.mgr.syntect_theme = self
|
|
||||||
.flavor
|
|
||||||
.syntect_path(light)
|
|
||||||
.or_else(|| normalize_path(self.mgr.syntect_theme))
|
|
||||||
.ok_or(anyhow!("[mgr].syntect_theme must be either empty or an absolute path."))?;
|
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn bg_color(&self) -> String { self.overall.bg.map(|c| c.to_string()).unwrap_or_default() }
|
pub fn bg_color(&self) -> String {
|
||||||
|
self.overall.get().bg.map(|c| c.to_string()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_syntect_theme<'de, D>(deserializer: D) -> Result<ArcSwap<PathBuf>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let mut path = PathBuf::deserialize(deserializer)?;
|
||||||
|
if !path.as_os_str().is_empty() {
|
||||||
|
path = normalize_path(path).ok_or_else(|| {
|
||||||
|
de::Error::custom("syntect_theme must be either empty or an absolute path.")
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(path.into_pointee())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_which_cols<'de, D>(deserializer: D) -> Result<SyncCell<u8>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let cols = u8::deserialize(deserializer)?;
|
||||||
|
if (1..=3).contains(&cols) {
|
||||||
|
Ok(SyncCell::new(cols))
|
||||||
|
} else {
|
||||||
|
Err(de::Error::custom("cols must be between 1 and 3"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(service services vfs);
|
yazi_macro::mod_flat!(service services sftp vfs);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use std::{io, mem, path::PathBuf};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::normalize_path;
|
use crate::vfs::ServiceSftp;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
|
@ -19,56 +17,3 @@ impl TryFrom<&'static Service> for &'static ServiceSftp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service {
|
|
||||||
pub(super) fn reshape(&mut self) -> io::Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Sftp(p) => p.reshape(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SFTP
|
|
||||||
#[derive(Deserialize, Eq, Hash, PartialEq, Serialize)]
|
|
||||||
pub struct ServiceSftp {
|
|
||||||
pub host: String,
|
|
||||||
pub user: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub password: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub key_file: PathBuf,
|
|
||||||
pub key_passphrase: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub cert_file: PathBuf,
|
|
||||||
#[serde(default)]
|
|
||||||
pub no_cert_verify: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub identity_agent: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServiceSftp {
|
|
||||||
fn reshape(&mut self) -> io::Result<()> {
|
|
||||||
if !self.key_file.as_os_str().is_empty() {
|
|
||||||
self.key_file = normalize_path(mem::take(&mut self.key_file))
|
|
||||||
.ok_or_else(|| io::Error::other("key_file must be either empty or an absolute path"))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.cert_file.as_os_str().is_empty() {
|
|
||||||
self.cert_file = normalize_path(mem::take(&mut self.cert_file))
|
|
||||||
.ok_or_else(|| io::Error::other("cert_file must be either empty or an absolute path"))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.identity_agent = if self.identity_agent.as_os_str().is_empty() {
|
|
||||||
std::env::var_os("SSH_AUTH_SOCK")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.filter(|p| p.is_absolute())
|
|
||||||
.unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
normalize_path(mem::take(&mut self.identity_agent)).ok_or_else(|| {
|
|
||||||
io::Error::other("identity_agent must be either empty or an absolute path")
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue