Merge branch 'sxyazi:main' into feat/independent-style-on-inner-section-of-status

This commit is contained in:
Anton Simonov 2024-03-05 20:19:22 +03:00 committed by GitHub
commit f5effc8845
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
139 changed files with 1940 additions and 1237 deletions

27
.github/workflows/cachix.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Cachix
on:
push:
branches:
- main
jobs:
publish:
name: Publish Flake
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v25
- name: Authenticate with Cachix
uses: cachix/cachix-action@v14
with:
name: yazi
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build Flake
run: nix build -L

View file

@ -6,9 +6,7 @@ on:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
release:
permissions:
contents: write
build-unix:
strategy:
matrix:
include:
@ -20,6 +18,27 @@ jobs:
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install gcc-aarch64-linux-gnu
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: sudo apt-get update && sudo apt-get install -yq gcc-aarch64-linux-gnu
- name: Build
run: ./scripts/build.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: yazi-${{ matrix.target }}.zip
path: yazi-${{ matrix.target }}.zip
build-windows:
strategy:
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
@ -31,57 +50,92 @@ jobs:
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal
- name: Add aarch64 target
if: contains(fromJson('["aarch64-unknown-linux-gnu", "aarch64-apple-darwin", "aarch64-pc-windows-msvc"]'), matrix.target)
- name: Add target
run: rustup target add ${{ matrix.target }}
- name: Install gcc-aarch64-linux-gnu
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -yq gcc-aarch64-linux-gnu
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
- name: Build
env:
YAZI_GEN_COMPLETIONS: true
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: /usr/bin/aarch64-linux-gnu-gcc
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Build snap
if: matrix.target == 'x86_64-unknown-linux-gnu'
uses: snapcore/action-build@v1
- name: Pack artifacts [Linux & macOS]
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
env:
TARGET_NAME: yazi-${{ matrix.target }}
run: |
mkdir $TARGET_NAME
cp target/${{ matrix.target }}/release/yazi $TARGET_NAME
cp -r yazi-config/completions $TARGET_NAME
cp README.md LICENSE $TARGET_NAME
zip -r $TARGET_NAME.zip $TARGET_NAME
- name: Pack artifacts [Windows]
- name: Pack artifact
if: matrix.os == 'windows-latest'
env:
TARGET_NAME: yazi-${{ matrix.target }}
run: |
New-Item -ItemType Directory -Path ${env:TARGET_NAME}
Copy-Item -Path "target\${{ matrix.target }}\release\yazi.exe" -Destination ${env:TARGET_NAME}
Copy-Item -Path "yazi-config\completions" -Destination ${env:TARGET_NAME} -Recurse
Copy-Item -Path "yazi-boot\completions" -Destination ${env:TARGET_NAME} -Recurse
Copy-Item -Path "README.md", "LICENSE" -Destination ${env:TARGET_NAME}
Compress-Archive -Path ${env:TARGET_NAME} -DestinationPath "${env:TARGET_NAME}.zip"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: yazi-${{ matrix.target }}.zip
path: yazi-${{ matrix.target }}.zip
build-musl:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- target: x86_64-unknown-linux-musl
image: rust-musl-cross:x86_64-musl
- target: aarch64-unknown-linux-musl
image: rust-musl-cross:aarch64-musl
container:
image: docker://ghcr.io/rust-cross/${{ matrix.image }}
steps:
- uses: actions/checkout@v4
- name: Build
run: ./scripts/build.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: yazi-${{ matrix.target }}.zip
path: yazi-${{ matrix.target }}.zip
build-snap:
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Build snap
uses: snapcore/action-build@v1
- name: Build
run: mv yazi_*.snap yazi-${{ matrix.target }}.snap
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: yazi-${{ matrix.target }}.snap
path: yazi-${{ matrix.target }}.snap
release:
permissions:
contents: write
runs-on: ubuntu-latest
needs: [build-unix, build-windows, build-musl, build-snap]
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
files: |
yazi-${{ matrix.target }}.zip
yazi*.snap
yazi-*.zip
yazi-*.snap
generate_release_notes: true

345
Cargo.lock generated
View file

@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.8"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
@ -72,9 +72,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.12"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
@ -120,15 +120,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.79"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]]
name = "arc-swap"
version = "1.6.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f"
[[package]]
name = "async-priority-channel"
@ -166,6 +166,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "better-panic"
version = "0.3.0"
@ -214,9 +220,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.9.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [
"memchr",
"serde",
@ -224,9 +230,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.0"
version = "3.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]]
name = "bytemuck"
@ -263,12 +269,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.83"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
[[package]]
name = "cfg-if"
@ -285,7 +288,7 @@ dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"windows-targets 0.52.0",
"windows-targets 0.52.4",
]
[[package]]
@ -307,7 +310,7 @@ dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"strsim 0.11.0",
]
[[package]]
@ -348,7 +351,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -431,9 +434,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.11"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
dependencies = [
"crossbeam-utils",
]
@ -505,6 +508,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.52",
]
[[package]]
name = "darling_macro"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.52",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -565,9 +603,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55d05712b2d8d88102bc9868020c9e5c7a1f5527c452b9b97450a1d006140ba7"
checksum = "388979d208a049ffdfb22fa33b9c81942215b940910bccfe258caeb25d125cb3"
dependencies = [
"serde",
]
@ -749,7 +787,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -805,9 +843,9 @@ dependencies = [
[[package]]
name = "gif"
version = "0.12.0"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
dependencies = [
"color_quant",
"weezl",
@ -827,9 +865,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "half"
version = "2.3.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
dependencies = [
"cfg-if",
"crunchy",
@ -853,9 +891,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.6"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "home"
@ -890,14 +928,10 @@ dependencies = [
]
[[package]]
name = "idna"
version = "0.4.0"
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
@ -909,17 +943,11 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "image"
version = "0.24.8"
version = "0.24.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
dependencies = [
"bytemuck",
"byteorder",
@ -941,9 +969,9 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
version = "2.2.3"
version = "2.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
dependencies = [
"equivalent",
"hashbrown",
@ -1001,9 +1029,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
@ -1093,15 +1121,15 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lru"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22"
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
dependencies = [
"hashbrown",
]
@ -1168,9 +1196,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
@ -1180,9 +1208,9 @@ dependencies = [
[[package]]
name = "mlua"
version = "0.9.5"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3561f79659ff3afad7b25e2bf2ec21507fe601ebecb7f81088669ec4bfd51e"
checksum = "868d02cb5eb97761bbf6bd6922c1c7a88b8ea252bbf43bd8350a0bf8497a1fc0"
dependencies = [
"bstr",
"erased-serde",
@ -1221,7 +1249,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -1434,7 +1462,7 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
dependencies = [
"base64",
"base64 0.21.7",
"indexmap",
"line-wrap",
"quick-xml",
@ -1444,9 +1472,9 @@ dependencies = [
[[package]]
name = "png"
version = "0.17.12"
version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c2378060fb13acff3ba0325b83442c1d2c44fbb76df481160ddc1687cce160"
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
@ -1543,9 +1571,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.8.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
dependencies = [
"either",
"rayon-core",
@ -1595,9 +1623,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
@ -1643,9 +1671,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "safemem"
@ -1670,9 +1698,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.196"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
@ -1689,20 +1717,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.196"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
name = "serde_json"
version = "1.0.113"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [
"itoa",
"ryu",
@ -1804,12 +1832,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "socket2"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
dependencies = [
"libc",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -1843,6 +1871,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strsim"
version = "0.11.0"
@ -1868,7 +1902,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -1884,9 +1918,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.49"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@ -1931,14 +1965,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
name = "thread_local"
version = "1.1.7"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
@ -2050,7 +2084,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -2143,7 +2177,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -2217,9 +2251,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
dependencies = [
"tinyvec",
]
@ -2243,7 +2277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna 0.5.0",
"idna",
"percent-encoding",
]
@ -2265,12 +2299,12 @@ dependencies = [
[[package]]
name = "validator"
version = "0.16.1"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd"
checksum = "da339118f018cc70ebf01fafc103360528aad53717e4bf311db929cb01cb9345"
dependencies = [
"idna 0.4.0",
"lazy_static",
"idna",
"once_cell",
"regex",
"serde",
"serde_derive",
@ -2281,28 +2315,16 @@ dependencies = [
[[package]]
name = "validator_derive"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
checksum = "76e88ea23b8f5e59230bff8a2f03c0ee0054a61d5b8343a38946bcd406fe624c"
dependencies = [
"if_chain",
"lazy_static",
"darling",
"proc-macro-error",
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
"validator_types",
]
[[package]]
name = "validator_types"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
dependencies = [
"proc-macro2",
"syn 1.0.109",
"syn 2.0.52",
]
[[package]]
@ -2331,9 +2353,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
@ -2347,9 +2369,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -2357,24 +2379,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2382,22 +2404,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "weezl"
@ -2464,7 +2486,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
"windows-targets 0.52.4",
]
[[package]]
@ -2482,7 +2504,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
"windows-targets 0.52.4",
]
[[package]]
@ -2517,17 +2539,17 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
]
[[package]]
@ -2544,9 +2566,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
@ -2562,9 +2584,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
@ -2580,9 +2602,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
@ -2598,9 +2620,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
@ -2616,9 +2638,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
@ -2634,9 +2656,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
@ -2652,15 +2674,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.6.1"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401"
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
dependencies = [
"memchr",
]
@ -2671,7 +2693,7 @@ version = "0.2.3"
dependencies = [
"anyhow",
"arc-swap",
"base64",
"base64 0.22.0",
"color_quant",
"crossterm",
"image",
@ -2722,7 +2744,7 @@ name = "yazi-core"
version = "0.2.3"
dependencies = [
"anyhow",
"base64",
"base64 0.22.0",
"bitflags 2.4.2",
"clipboard-win",
"crossterm",
@ -2743,6 +2765,7 @@ dependencies = [
"yazi-boot",
"yazi-config",
"yazi-plugin",
"yazi-proxy",
"yazi-scheduler",
"yazi-shared",
]
@ -2773,6 +2796,7 @@ dependencies = [
"yazi-config",
"yazi-core",
"yazi-plugin",
"yazi-proxy",
"yazi-scheduler",
"yazi-shared",
]
@ -2804,6 +2828,7 @@ dependencies = [
"yazi-boot",
"yazi-config",
"yazi-prebuild",
"yazi-proxy",
"yazi-shared",
]
@ -2813,13 +2838,24 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4b6c8e12e39ac0f79fa96f36e5b88e0da8d230691abd729eec709b43c74f632"
[[package]]
name = "yazi-proxy"
version = "0.2.3"
dependencies = [
"anyhow",
"mlua",
"tokio",
"yazi-config",
"yazi-shared",
]
[[package]]
name = "yazi-scheduler"
version = "0.2.3"
dependencies = [
"anyhow",
"async-priority-channel",
"base64",
"base64 0.22.0",
"crossterm",
"futures",
"parking_lot",
@ -2831,6 +2867,7 @@ dependencies = [
"yazi-adaptor",
"yazi-config",
"yazi-plugin",
"yazi-proxy",
"yazi-shared",
]
@ -2869,7 +2906,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]

View file

@ -12,10 +12,10 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
- 💪 **Powerful Async Task Scheduling and Management**: Provides real-time progress updates, task cancellation, and internal task priority assignment.
- 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++, covering almost all terminals.
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins (coming soon), custom previewer, and custom preloader; Just some pieces of Lua.
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer, and custom preloader; Just some pieces of Lua.
- 🧰 Integration with fd, rg, fzf, zoxide
- 💫 Vim-like input/select component, auto-completion for cd paths
- 🏷️ Multi-Tab Support, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.)
- 💫 Vim-like input/select/notify component, auto-completion for cd paths
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.)
- 🔄 Bulk Renaming, Visual Mode, File Chooser
- 🎨 Theme System, Custom Layouts, Trash Bin, CSI u
- ... and more!
@ -46,8 +46,9 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265
| foot | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
| Ghostty | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in |
| Black Box | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
| Tabby | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
| Hyper | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
| VSCode | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
| Tabby | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
| Hyper | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
| X11 / Wayland | Window system protocol | ☑️ Überzug++ required |
| Fallback | [Chafa](https://hpjansson.org/chafa/) | ☑️ Überzug++ required |

View file

@ -60,9 +60,9 @@
wrapProgram $out/bin/yazi \
--prefix PATH : "${makeBinPath runtimePaths}"
installShellCompletion --cmd yazi \
--bash ./yazi-config/completions/yazi.bash \
--fish ./yazi-config/completions/yazi.fish \
--zsh ./yazi-config/completions/_yazi
--bash ./yazi-boot/completions/yazi.bash \
--fish ./yazi-boot/completions/yazi.fish \
--zsh ./yazi-boot/completions/_yazi
# Resize logo
for RES in 16 24 32 48 64 128 256; do

View file

@ -1,20 +1,26 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $SCRIPT_DIR/..
export ARTIFACT_NAME="yazi-$1"
export YAZI_GEN_COMPLETIONS=1
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc
cargo +stable build --release --target aarch64-apple-darwin
cargo +stable build --release --target x86_64-apple-darwin
cargo +stable build --release --target x86_64-unknown-linux-gnu
cargo +stable build --release --target x86_64-pc-windows-gnu
# Setup Rust toolchain
rustup toolchain install stable --profile minimal
rustup target add "$1"
mv target/aarch64-apple-darwin/release/yazi yazi-aarch64-apple-darwin
mv target/x86_64-apple-darwin/release/yazi yazi-x86_64-apple-darwin
mv target/x86_64-unknown-linux-gnu/release/yazi yazi-x86_64-unknown-linux-gnu
mv target/x86_64-pc-windows-gnu/release/yazi.exe yazi-x86_64-pc-windows-gnu.exe
# Build for the target
cargo build --release --locked --target "$1"
zip -j yazi-aarch64-apple-darwin.zip yazi-aarch64-apple-darwin
zip -j yazi-x86_64-apple-darwin.zip yazi-x86_64-apple-darwin
zip -j yazi-x86_64-unknown-linux-gnu.zip yazi-x86_64-unknown-linux-gnu
zip -j yazi-x86_64-pc-windows-gnu.zip yazi-x86_64-pc-windows-gnu.exe
# Create the artifact
mkdir "$ARTIFACT_NAME"
cp "target/$1/release/yazi" "$ARTIFACT_NAME"
cp -r yazi-boot/completions "$ARTIFACT_NAME"
cp README.md LICENSE "$ARTIFACT_NAME"
# Zip the artifact
if ! command -v zip &> /dev/null
then
sudo apt-get update && sudo apt-get install -yq zip
fi
zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME"

View file

@ -92,9 +92,9 @@ impl Adaptor {
Emulator::Foot => vec![Self::Sixel],
Emulator::Ghostty => vec![Self::KittyOld],
Emulator::BlackBox => vec![Self::Sixel],
Emulator::VSCode => vec![Self::Sixel],
Emulator::Tabby => vec![Self::Sixel],
Emulator::Hyper => vec![Self::Sixel],
Emulator::VSCode => vec![Self::Iterm2, Self::Sixel],
Emulator::Tabby => vec![Self::Iterm2, Self::Sixel],
Emulator::Hyper => vec![Self::Iterm2, Self::Sixel],
Emulator::Mintty => vec![Self::Iterm2],
Emulator::Neovim => vec![],
};

View file

@ -1,7 +1,7 @@
#[path = "src/args.rs"]
mod args;
use std::{env, error::Error, fs};
use std::{env, error::Error};
use clap::CommandFactory;
use clap_complete::{generate_to, Shell};
@ -18,7 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let bin = "yazi";
let out = "completions";
fs::create_dir_all(out)?;
std::fs::create_dir_all(out)?;
generate_to(Shell::Bash, cmd, bin, out)?;
generate_to(Shell::Fish, cmd, bin, out)?;
generate_to(Shell::Zsh, cmd, bin, out)?;

View file

@ -1,4 +1,4 @@
use std::{ffi::OsString, fs, path::{Path, PathBuf}, process};
use std::{ffi::OsString, path::{Path, PathBuf}, process};
use clap::Parser;
use serde::Serialize;
@ -14,8 +14,8 @@ pub struct Boot {
pub file: Option<OsString>,
pub config_dir: PathBuf,
pub flavor_dir: PathBuf,
pub plugin_dir: PathBuf,
pub state_dir: PathBuf,
}
impl Boot {
@ -36,20 +36,20 @@ impl Boot {
impl Default for Boot {
fn default() -> Self {
let config_dir = Xdg::config_dir();
let (cwd, file) = Self::parse_entry(ARGS.entry.as_deref());
let boot = Self {
cwd,
file,
config_dir: Xdg::config_dir().unwrap(),
plugin_dir: Xdg::plugin_dir().unwrap(),
state_dir: Xdg::state_dir().unwrap(),
flavor_dir: config_dir.join("flavors"),
plugin_dir: config_dir.join("plugins"),
config_dir,
};
if !boot.state_dir.is_dir() {
fs::create_dir_all(&boot.state_dir).unwrap();
}
std::fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory");
std::fs::create_dir_all(&boot.plugin_dir).expect("Failed to create plugin directory");
boot
}
}
@ -71,7 +71,7 @@ impl Default for Args {
if args.clear_cache {
if PREVIEW.cache_dir == Xdg::cache_dir() {
println!("Clearing cache directory: \n{:?}", PREVIEW.cache_dir);
fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
std::fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
} else {
println!(
"You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}",

View file

@ -5,296 +5,301 @@
[manager]
keymap = [
{ on = [ "<Esc>" ], exec = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = [ "q" ], exec = "quit", desc = "Exit the process" },
{ on = [ "Q" ], exec = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
{ on = [ "<C-q>" ], exec = "close", desc = "Close the current tab, or quit if it is last tab" },
{ on = [ "<C-z>" ], exec = "suspend", desc = "Suspend the process" },
{ on = [ "<Esc>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = [ "<C-[>" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" },
{ on = [ "q" ], run = "quit", desc = "Exit the process" },
{ on = [ "Q" ], run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" },
{ on = [ "<C-q>" ], run = "close", desc = "Close the current tab, or quit if it is last tab" },
{ on = [ "<C-z>" ], run = "suspend", desc = "Suspend the process" },
# Navigation
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<S-Up>" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<C-u>" ], exec = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-d>" ], exec = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<C-b>" ], exec = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<C-f>" ], exec = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "<C-u>" ], run = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-d>" ], run = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<C-b>" ], run = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<C-f>" ], run = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "<C-PageUp>" ], exec = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-PageDown>" ], exec = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<PageUp>" ], exec = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<PageDown>" ], exec = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "<C-PageUp>" ], run = "arrow -50%", desc = "Move cursor up half page" },
{ on = [ "<C-PageDown>" ], run = "arrow 50%", desc = "Move cursor down half page" },
{ on = [ "<PageUp>" ], run = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<PageDown>" ], run = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" },
{ on = [ "l" ], exec = "enter", desc = "Enter the child directory" },
{ on = [ "h" ], run = "leave", desc = "Go back to the parent directory" },
{ on = [ "l" ], run = "enter", desc = "Enter the child directory" },
{ on = [ "H" ], exec = "back", desc = "Go back to the previous directory" },
{ on = [ "L" ], exec = "forward", desc = "Go forward to the next directory" },
{ on = [ "H" ], run = "back", desc = "Go back to the previous directory" },
{ on = [ "L" ], run = "forward", desc = "Go forward to the next directory" },
{ on = [ "<A-k>" ], exec = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-j>" ], exec = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<A-PageUp>" ], exec = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-PageDown>" ], exec = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<A-k>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-j>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<A-PageUp>" ], run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = [ "<A-PageDown>" ], run = "seek 5", desc = "Seek down 5 units in the preview" },
{ on = [ "<Up>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Left>" ], exec = "leave", desc = "Go back to the parent directory" },
{ on = [ "<Right>" ], exec = "enter", desc = "Enter the child directory" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Left>" ], run = "leave", desc = "Go back to the parent directory" },
{ on = [ "<Right>" ], run = "enter", desc = "Enter the child directory" },
{ on = [ "g", "g" ], exec = "arrow -99999999", desc = "Move cursor to the top" },
{ on = [ "G" ], exec = "arrow 99999999", desc = "Move cursor to the bottom" },
{ on = [ "g", "g" ], run = "arrow -99999999", desc = "Move cursor to the top" },
{ on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom" },
# Selection
{ on = [ "<Space>" ], exec = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = [ "v" ], exec = "visual_mode", desc = "Enter visual mode (selection mode)" },
{ on = [ "V" ], exec = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
{ on = [ "<C-a>" ], exec = "select_all --state=true", desc = "Select all files" },
{ on = [ "<C-r>" ], exec = "select_all --state=none", desc = "Inverse selection of all files" },
{ on = [ "<Space>" ], run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = [ "v" ], run = "visual_mode", desc = "Enter visual mode (selection mode)" },
{ on = [ "V" ], run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },
{ on = [ "<C-a>" ], run = "select_all --state=true", desc = "Select all files" },
{ on = [ "<C-r>" ], run = "select_all --state=none", desc = "Inverse selection of all files" },
# Operation
{ on = [ "o" ], exec = "open", desc = "Open the selected files" },
{ on = [ "O" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], exec = "open", desc = "Open the selected files" },
{ on = [ "<C-Enter>" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "y" ], exec = "yank", desc = "Copy the selected files" },
{ on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "x" ], exec = "yank --cut", desc = "Cut the selected files" },
{ on = [ "p" ], exec = "paste", desc = "Paste the files" },
{ on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
{ on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" },
{ on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], exec = "remove", desc = "Move the files to the trash" },
{ on = [ "D" ], exec = "remove --permanently", desc = "Permanently delete the files" },
{ on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" },
{ on = [ "r" ], exec = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = [ ";" ], exec = "shell", desc = "Run a shell command" },
{ on = [ ":" ], exec = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" },
{ on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" },
{ on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" },
{ on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" },
{ on = [ "<C-s>" ], exec = "search none", desc = "Cancel the ongoing search" },
{ on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" },
{ on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" },
{ on = [ "o" ], run = "open", desc = "Open the selected files" },
{ on = [ "O" ], run = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], run = "open", desc = "Open the selected files" },
{ on = [ "<C-Enter>" ], run = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "y" ], run = "yank", desc = "Copy the selected files" },
{ on = [ "Y" ], run = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "x" ], run = "yank --cut", desc = "Cut the selected files" },
{ on = [ "p" ], run = "paste", desc = "Paste the files" },
{ on = [ "P" ], run = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
{ on = [ "-" ], run = "link", desc = "Symlink the absolute path of files" },
{ on = [ "_" ], run = "link --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], run = "remove", desc = "Move the files to the trash" },
{ on = [ "D" ], run = "remove --permanently", desc = "Permanently delete the files" },
{ on = [ "a" ], run = "create", desc = "Create a file or directory (ends with / for directories)" },
{ on = [ "r" ], run = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = [ ";" ], run = "shell", desc = "Run a shell command" },
{ on = [ ":" ], run = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" },
{ on = [ "." ], run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
{ on = [ "s" ], run = "search fd", desc = "Search files by name using fd" },
{ on = [ "S" ], run = "search rg", desc = "Search files by content using ripgrep" },
{ on = [ "<C-s>" ], run = "search none", desc = "Cancel the ongoing search" },
{ on = [ "z" ], run = "jump zoxide", desc = "Jump to a directory using zoxide" },
{ on = [ "Z" ], run = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" },
# Linemode
{ on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" },
{ on = [ "m", "p" ], exec = "linemode permissions", desc = "Set linemode to permissions" },
{ on = [ "m", "m" ], exec = "linemode mtime", desc = "Set linemode to mtime" },
{ on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" },
{ on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" },
{ on = [ "m", "p" ], run = "linemode permissions", desc = "Set linemode to permissions" },
{ on = [ "m", "m" ], run = "linemode mtime", desc = "Set linemode to mtime" },
{ on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" },
# Copy
{ on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" },
{ on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" },
{ on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" },
{ on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
{ on = [ "c", "c" ], run = "copy path", desc = "Copy the absolute path" },
{ on = [ "c", "d" ], run = "copy dirname", desc = "Copy the path of the parent directory" },
{ on = [ "c", "f" ], run = "copy filename", desc = "Copy the name of the file" },
{ on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
# Filter
{ on = [ "f" ], exec = "filter --smart", desc = "Filter the files" },
{ on = [ "f" ], run = "filter --smart", desc = "Filter the files" },
# Find
{ on = [ "/" ], exec = "find --smart", desc = "Find next file" },
{ on = [ "?" ], exec = "find --previous --smart", desc = "Find previous file" },
{ on = [ "n" ], exec = "find_arrow", desc = "Go to next found file" },
{ on = [ "N" ], exec = "find_arrow --previous", desc = "Go to previous found file" },
{ on = [ "/" ], run = "find --smart", desc = "Find next file" },
{ on = [ "?" ], run = "find --previous --smart", desc = "Find previous file" },
{ on = [ "n" ], run = "find_arrow", desc = "Go to next found file" },
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
# Sorting
{ on = [ ",", "m" ], exec = "sort modified --dir-first", desc = "Sort by modified time" },
{ on = [ ",", "M" ], exec = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
{ on = [ ",", "c" ], exec = "sort created --dir-first", desc = "Sort by created time" },
{ on = [ ",", "C" ], exec = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
{ on = [ ",", "e" ], exec = "sort extension --dir-first", desc = "Sort by extension" },
{ on = [ ",", "E" ], exec = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
{ on = [ ",", "a" ], exec = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
{ on = [ ",", "A" ], exec = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
{ on = [ ",", "n" ], exec = "sort natural --dir-first", desc = "Sort naturally" },
{ on = [ ",", "N" ], exec = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
{ on = [ ",", "s" ], exec = "sort size --dir-first", desc = "Sort by size" },
{ on = [ ",", "S" ], exec = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
{ on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" },
{ on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
{ on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" },
{ on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
{ on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" },
{ on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
{ on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
{ on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
{ on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" },
{ on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
{ on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" },
{ on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
# Tabs
{ on = [ "t" ], exec = "tab_create --current", desc = "Create a new tab using the current path" },
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
{ on = [ "1" ], exec = "tab_switch 0", desc = "Switch to the first tab" },
{ on = [ "2" ], exec = "tab_switch 1", desc = "Switch to the second tab" },
{ on = [ "3" ], exec = "tab_switch 2", desc = "Switch to the third tab" },
{ on = [ "4" ], exec = "tab_switch 3", desc = "Switch to the fourth tab" },
{ on = [ "5" ], exec = "tab_switch 4", desc = "Switch to the fifth tab" },
{ on = [ "6" ], exec = "tab_switch 5", desc = "Switch to the sixth tab" },
{ on = [ "7" ], exec = "tab_switch 6", desc = "Switch to the seventh tab" },
{ on = [ "8" ], exec = "tab_switch 7", desc = "Switch to the eighth tab" },
{ on = [ "9" ], exec = "tab_switch 8", desc = "Switch to the ninth tab" },
{ on = [ "1" ], run = "tab_switch 0", desc = "Switch to the first tab" },
{ on = [ "2" ], run = "tab_switch 1", desc = "Switch to the second tab" },
{ on = [ "3" ], run = "tab_switch 2", desc = "Switch to the third tab" },
{ on = [ "4" ], run = "tab_switch 3", desc = "Switch to the fourth tab" },
{ on = [ "5" ], run = "tab_switch 4", desc = "Switch to the fifth tab" },
{ on = [ "6" ], run = "tab_switch 5", desc = "Switch to the sixth tab" },
{ on = [ "7" ], run = "tab_switch 6", desc = "Switch to the seventh tab" },
{ on = [ "8" ], run = "tab_switch 7", desc = "Switch to the eighth tab" },
{ on = [ "9" ], run = "tab_switch 8", desc = "Switch to the ninth tab" },
{ on = [ "[" ], exec = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
{ on = [ "]" ], exec = "tab_switch 1 --relative", desc = "Switch to the next tab" },
{ on = [ "[" ], run = "tab_switch -1 --relative", desc = "Switch to the previous tab" },
{ on = [ "]" ], run = "tab_switch 1 --relative", desc = "Switch to the next tab" },
{ on = [ "{" ], exec = "tab_swap -1", desc = "Swap the current tab with the previous tab" },
{ on = [ "}" ], exec = "tab_swap 1", desc = "Swap the current tab with the next tab" },
{ on = [ "{" ], run = "tab_swap -1", desc = "Swap the current tab with the previous tab" },
{ on = [ "}" ], run = "tab_swap 1", desc = "Swap the current tab with the next tab" },
# Tasks
{ on = [ "w" ], exec = "tasks_show", desc = "Show the tasks manager" },
{ on = [ "w" ], run = "tasks_show", desc = "Show the tasks manager" },
# Goto
{ on = [ "g", "h" ], exec = "cd ~", desc = "Go to the home directory" },
{ on = [ "g", "c" ], exec = "cd ~/.config", desc = "Go to the config directory" },
{ on = [ "g", "d" ], exec = "cd ~/Downloads", desc = "Go to the downloads directory" },
{ on = [ "g", "t" ], exec = "cd /tmp", desc = "Go to the temporary directory" },
{ on = [ "g", "<Space>" ], exec = "cd --interactive", desc = "Go to a directory interactively" },
{ on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" },
{ on = [ "g", "c" ], run = "cd ~/.config", desc = "Go to the config directory" },
{ on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go to the downloads directory" },
{ on = [ "g", "t" ], run = "cd /tmp", desc = "Go to the temporary directory" },
{ on = [ "g", "<Space>" ], run = "cd --interactive", desc = "Go to a directory interactively" },
# Help
{ on = [ "~" ], exec = "help", desc = "Open help" },
{ on = [ "~" ], run = "help", desc = "Open help" },
]
[tasks]
keymap = [
{ on = [ "<Esc>" ], exec = "close", desc = "Hide the task manager" },
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the task manager" },
{ on = [ "w" ], exec = "close", desc = "Hide the task manager" },
{ on = [ "<Esc>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "<C-[>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "<C-q>" ], run = "close", desc = "Hide the task manager" },
{ on = [ "w" ], run = "close", desc = "Hide the task manager" },
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Enter>" ], exec = "inspect", desc = "Inspect the task" },
{ on = [ "x" ], exec = "cancel", desc = "Cancel the task" },
{ on = [ "<Enter>" ], run = "inspect", desc = "Inspect the task" },
{ on = [ "x" ], run = "cancel", desc = "Cancel the task" },
{ on = [ "~" ], exec = "help", desc = "Open help" }
{ on = [ "~" ], run = "help", desc = "Open help" }
]
[select]
keymap = [
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel selection" },
{ on = [ "<Esc>" ], exec = "close", desc = "Cancel selection" },
{ on = [ "<Enter>" ], exec = "close --submit", desc = "Submit the selection" },
{ on = [ "<Esc>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<C-[>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<C-q>" ], run = "close", desc = "Cancel selection" },
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the selection" },
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<Up>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<S-Up>" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "~" ], exec = "help", desc = "Open help" }
{ on = [ "~" ], run = "help", desc = "Open help" }
]
[input]
keymap = [
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel input" },
{ on = [ "<Enter>" ], exec = "close --submit", desc = "Submit the input" },
{ on = [ "<Esc>" ], exec = "escape", desc = "Go back the normal mode, or cancel input" },
{ on = [ "<C-q>" ], run = "close", desc = "Cancel input" },
{ on = [ "<Enter>" ], run = "close --submit", desc = "Submit the input" },
{ on = [ "<Esc>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
{ on = [ "<C-[>" ], run = "escape", desc = "Go back the normal mode, or cancel input" },
# Mode
{ on = [ "i" ], exec = "insert", desc = "Enter insert mode" },
{ on = [ "a" ], exec = "insert --append", desc = "Enter append mode" },
{ on = [ "I" ], exec = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
{ on = [ "A" ], exec = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
{ on = [ "v" ], exec = "visual", desc = "Enter visual mode" },
{ on = [ "V" ], exec = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
{ on = [ "i" ], run = "insert", desc = "Enter insert mode" },
{ on = [ "a" ], run = "insert --append", desc = "Enter append mode" },
{ on = [ "I" ], run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
{ on = [ "A" ], run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
{ on = [ "v" ], run = "visual", desc = "Enter visual mode" },
{ on = [ "V" ], run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
# Character-wise movement
{ on = [ "h" ], exec = "move -1", desc = "Move back a character" },
{ on = [ "l" ], exec = "move 1", desc = "Move forward a character" },
{ on = [ "<Left>" ], exec = "move -1", desc = "Move back a character" },
{ on = [ "<Right>" ], exec = "move 1", desc = "Move forward a character" },
{ on = [ "<C-b>" ], exec = "move -1", desc = "Move back a character" },
{ on = [ "<C-f>" ], exec = "move 1", desc = "Move forward a character" },
{ on = [ "h" ], run = "move -1", desc = "Move back a character" },
{ on = [ "l" ], run = "move 1", desc = "Move forward a character" },
{ on = [ "<Left>" ], run = "move -1", desc = "Move back a character" },
{ on = [ "<Right>" ], run = "move 1", desc = "Move forward a character" },
{ on = [ "<C-b>" ], run = "move -1", desc = "Move back a character" },
{ on = [ "<C-f>" ], run = "move 1", desc = "Move forward a character" },
# Word-wise movement
{ on = [ "b" ], exec = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "w" ], exec = "forward", desc = "Move forward to the start of the next word" },
{ on = [ "e" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = [ "<A-b>" ], exec = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "<A-f>" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = [ "b" ], run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "w" ], run = "forward", desc = "Move forward to the start of the next word" },
{ on = [ "e" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
{ on = [ "<A-b>" ], run = "backward", desc = "Move back to the start of the current or previous word" },
{ on = [ "<A-f>" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
# Line-wise movement
{ on = [ "0" ], exec = "move -999", desc = "Move to the BOL" },
{ on = [ "$" ], exec = "move 999", desc = "Move to the EOL" },
{ on = [ "<C-a>" ], exec = "move -999", desc = "Move to the BOL" },
{ on = [ "<C-e>" ], exec = "move 999", desc = "Move to the EOL" },
{ on = [ "<Home>" ], exec = "move -999", desc = "Move to the BOL" },
{ on = [ "<End>" ], exec = "move 999", desc = "Move to the EOL" },
{ on = [ "0" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "$" ], run = "move 999", desc = "Move to the EOL" },
{ on = [ "<C-a>" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "<C-e>" ], run = "move 999", desc = "Move to the EOL" },
{ on = [ "<Home>" ], run = "move -999", desc = "Move to the BOL" },
{ on = [ "<End>" ], run = "move 999", desc = "Move to the EOL" },
# Delete
{ on = [ "<Backspace>" ], exec = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<Delete>" ], exec = "backspace --under", desc = "Delete the character under the cursor" },
{ on = [ "<C-h>" ], exec = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<C-d>" ], exec = "backspace --under", desc = "Delete the character under the cursor" },
{ on = [ "<Backspace>" ], run = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<Delete>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
{ on = [ "<C-h>" ], run = "backspace", desc = "Delete the character before the cursor" },
{ on = [ "<C-d>" ], run = "backspace --under", desc = "Delete the character under the cursor" },
# Kill
{ on = [ "<C-u>" ], exec = "kill bol", desc = "Kill backwards to the BOL" },
{ on = [ "<C-k>" ], exec = "kill eol", desc = "Kill forwards to the EOL" },
{ on = [ "<C-w>" ], exec = "kill backward", desc = "Kill backwards to the start of the current word" },
{ on = [ "<A-d>" ], exec = "kill forward", desc = "Kill forwards to the end of the current word" },
{ on = [ "<C-u>" ], run = "kill bol", desc = "Kill backwards to the BOL" },
{ on = [ "<C-k>" ], run = "kill eol", desc = "Kill forwards to the EOL" },
{ on = [ "<C-w>" ], run = "kill backward", desc = "Kill backwards to the start of the current word" },
{ on = [ "<A-d>" ], run = "kill forward", desc = "Kill forwards to the end of the current word" },
# Cut/Yank/Paste
{ on = [ "d" ], exec = "delete --cut", desc = "Cut the selected characters" },
{ on = [ "D" ], exec = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
{ on = [ "c" ], exec = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
{ on = [ "C" ], exec = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
{ on = [ "x" ], exec = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
{ on = [ "y" ], exec = "yank", desc = "Copy the selected characters" },
{ on = [ "p" ], exec = "paste", desc = "Paste the copied characters after the cursor" },
{ on = [ "P" ], exec = "paste --before", desc = "Paste the copied characters before the cursor" },
{ on = [ "d" ], run = "delete --cut", desc = "Cut the selected characters" },
{ on = [ "D" ], run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
{ on = [ "c" ], run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
{ on = [ "C" ], run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
{ on = [ "x" ], run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
{ on = [ "y" ], run = "yank", desc = "Copy the selected characters" },
{ on = [ "p" ], run = "paste", desc = "Paste the copied characters after the cursor" },
{ on = [ "P" ], run = "paste --before", desc = "Paste the copied characters before the cursor" },
# Undo/Redo
{ on = [ "u" ], exec = "undo", desc = "Undo the last operation" },
{ on = [ "<C-r>" ], exec = "redo", desc = "Redo the last operation" },
{ on = [ "u" ], run = "undo", desc = "Undo the last operation" },
{ on = [ "<C-r>" ], run = "redo", desc = "Redo the last operation" },
# Help
{ on = [ "~" ], exec = "help", desc = "Open help" }
{ on = [ "~" ], run = "help", desc = "Open help" }
]
[completion]
keymap = [
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel completion" },
{ on = [ "<Tab>" ], exec = "close --submit", desc = "Submit the completion" },
{ on = [ "<Enter>" ], exec = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
{ on = [ "<C-q>" ], run = "close", desc = "Cancel completion" },
{ on = [ "<Tab>" ], run = "close --submit", desc = "Submit the completion" },
{ on = [ "<Enter>" ], run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
{ on = [ "<A-k>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<A-j>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<A-k>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<A-j>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "~" ], exec = "help", desc = "Open help" }
{ on = [ "~" ], run = "help", desc = "Open help" }
]
[help]
keymap = [
{ on = [ "<Esc>" ], exec = "escape", desc = "Clear the filter, or hide the help" },
{ on = [ "q" ], exec = "close", desc = "Exit the process" },
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the help" },
{ on = [ "<Esc>" ], run = "escape", desc = "Clear the filter, or hide the help" },
{ on = [ "<C-[>" ], run = "escape", desc = "Clear the filter, or hide the help" },
{ on = [ "q" ], run = "close", desc = "Exit the process" },
{ on = [ "<C-q>" ], run = "close", desc = "Hide the help" },
# Navigation
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "k" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "j" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<Up>" ], exec = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], exec = "arrow 1", desc = "Move cursor down" },
{ on = [ "<Up>" ], run = "arrow -1", desc = "Move cursor up" },
{ on = [ "<Down>" ], run = "arrow 1", desc = "Move cursor down" },
{ on = [ "<S-Up>" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
{ on = [ "<S-Up>" ], run = "arrow -5", desc = "Move cursor up 5 lines" },
{ on = [ "<S-Down>" ], run = "arrow 5", desc = "Move cursor down 5 lines" },
# Filtering
{ on = [ "/" ], exec = "filter", desc = "Apply a filter for the help items" },
{ on = [ "/" ], run = "filter", desc = "Apply a filter for the help items" },
]

View file

@ -4,6 +4,13 @@
# vim:fileencoding=utf-8:foldmethod=marker
# : Flavor {{{
[flavor]
use = ""
# : }}}
# : Manager {{{
[manager]
@ -137,7 +144,7 @@ separator_style = { fg = "darkgray" }
[help]
on = { fg = "magenta" }
exec = { fg = "cyan" }
run = { fg = "cyan" }
desc = { fg = "gray" }
hovered = { bg = "darkgray", bold = true }
footer = { fg = "black", bg = "white" }
@ -145,6 +152,21 @@ footer = { fg = "black", bg = "white" }
# : }}}
# : Notify {{{
[notify]
title_info = { fg = "green" }
title_warn = { fg = "yellow" }
title_error = { fg = "red" }
# Icons
icon_info = ""
icon_warn = ""
icon_error = ""
# : }}}
# : File-specific styles {{{
[filetype]

View file

@ -26,27 +26,28 @@ ueberzug_offset = [ 0, 0, 0, 0 ]
[opener]
edit = [
{ exec = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
{ exec = 'code "%*"', orphan = true, for = "windows" },
{ run = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
{ run = 'code "%*"', orphan = true, desc = "code", for = "windows" },
{ run = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" },
]
open = [
{ exec = 'xdg-open "$@"', desc = "Open", for = "linux" },
{ exec = 'open "$@"', desc = "Open", for = "macos" },
{ exec = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" }
{ run = 'xdg-open "$@"', desc = "Open", for = "linux" },
{ run = 'open "$@"', desc = "Open", for = "macos" },
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
]
reveal = [
{ exec = 'open -R "$1"', desc = "Reveal", for = "macos" },
{ exec = 'explorer /select, "%1"', orphan = true, desc = "Reveal", for = "windows" },
{ exec = '''exiftool "$1"; echo "Press enter to exit"; read''', block = true, desc = "Show EXIF", for = "unix" },
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
{ run = 'explorer /select, "%1"', orphan = true, desc = "Reveal", for = "windows" },
{ run = '''exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
]
extract = [
{ exec = 'unar "$1"', desc = "Extract here", for = "unix" },
{ exec = 'unar "%1"', desc = "Extract here", for = "windows" },
{ run = 'unar "$1"', desc = "Extract here", for = "unix" },
{ run = 'unar "%1"', desc = "Extract here", for = "windows" },
]
play = [
{ exec = 'mpv "$@"', orphan = true, for = "unix" },
{ exec = 'mpv "%1"', orphan = true, for = "windows" },
{ exec = '''mediainfo "$1"; echo "Press enter to exit"; read''', block = true, desc = "Show media info", for = "unix" },
{ run = 'mpv "$@"', orphan = true, for = "unix" },
{ run = 'mpv "%1"', orphan = true, for = "windows" },
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
]
[open]
@ -85,42 +86,42 @@ suppress_preload = false
[plugin]
preloaders = [
{ name = "*", cond = "!mime", exec = "mime", multi = true, prio = "high" },
{ name = "*", cond = "!mime", run = "mime", multi = true, prio = "high" },
# Image
{ mime = "image/vnd.djvu", exec = "noop" },
{ mime = "image/*", exec = "image" },
{ mime = "image/vnd.djvu", run = "noop" },
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", exec = "video" },
{ mime = "video/*", run = "video" },
# PDF
{ mime = "application/pdf", exec = "pdf" },
{ mime = "application/pdf", run = "pdf" },
]
previewers = [
{ name = "*/", exec = "folder", sync = true },
{ name = "*/", run = "folder", sync = true },
# Code
{ mime = "text/*", exec = "code" },
{ mime = "*/xml", exec = "code" },
{ mime = "*/javascript", exec = "code" },
{ mime = "*/x-wine-extension-ini", exec = "code" },
{ mime = "text/*", run = "code" },
{ mime = "*/xml", run = "code" },
{ mime = "*/javascript", run = "code" },
{ mime = "*/x-wine-extension-ini", run = "code" },
# JSON
{ mime = "application/json", exec = "json" },
{ mime = "application/json", run = "json" },
# Image
{ mime = "image/vnd.djvu", exec = "noop" },
{ mime = "image/*", exec = "image" },
{ mime = "image/vnd.djvu", run = "noop" },
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", exec = "video" },
{ mime = "video/*", run = "video" },
# PDF
{ mime = "application/pdf", exec = "pdf" },
{ mime = "application/pdf", run = "pdf" },
# Archive
{ mime = "application/zip", exec = "archive" },
{ mime = "application/gzip", exec = "archive" },
{ mime = "application/x-tar", exec = "archive" },
{ mime = "application/x-bzip", exec = "archive" },
{ mime = "application/x-bzip2", exec = "archive" },
{ mime = "application/x-7z-compressed", exec = "archive" },
{ mime = "application/x-rar", exec = "archive" },
{ mime = "application/xz", exec = "archive" },
{ mime = "application/zip", run = "archive" },
{ mime = "application/gzip", run = "archive" },
{ mime = "application/x-tar", run = "archive" },
{ mime = "application/x-bzip", run = "archive" },
{ mime = "application/x-bzip2", run = "archive" },
{ mime = "application/x-7z-compressed", run = "archive" },
{ mime = "application/x-rar", run = "archive" },
{ mime = "application/xz", run = "archive" },
# Fallback
{ name = "*", exec = "file" },
{ name = "*", run = "file" },
]
[input]

View file

@ -1,22 +1,21 @@
use std::{borrow::Cow, collections::VecDeque, ops::Deref};
use std::{borrow::Cow, collections::VecDeque};
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use yazi_shared::event::Cmd;
use super::Key;
#[derive(Debug, Default, Deserialize)]
#[derive(Debug, Default)]
pub struct Control {
pub on: Vec<Key>,
#[serde(deserialize_with = "super::exec_deserialize")]
pub exec: Vec<Cmd>,
pub run: Vec<Cmd>,
pub desc: Option<String>,
}
impl Control {
#[inline]
pub fn to_seq(&self) -> VecDeque<Cmd> {
self.exec.iter().map(|e| e.clone_without_data()).collect()
self.run.iter().map(|e| e.clone_without_data()).collect()
}
}
@ -25,58 +24,47 @@ impl Control {
pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() }
#[inline]
pub fn exec(&self) -> String {
self.exec.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
pub fn run(&self) -> String {
self.run.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
}
#[inline]
pub fn desc_or_exec(&self) -> Cow<str> {
if let Some(ref s) = self.desc { Cow::Borrowed(s) } else { self.exec().into() }
pub fn desc_or_run(&self) -> Cow<str> {
if let Some(ref s) = self.desc { Cow::Borrowed(s) } else { self.run().into() }
}
#[inline]
pub fn contains(&self, s: &str) -> bool {
let s = s.to_lowercase();
self.desc.as_ref().map(|d| d.to_lowercase().contains(&s)) == Some(true)
|| self.exec().to_lowercase().contains(&s)
|| self.run().to_lowercase().contains(&s)
|| self.on().to_lowercase().contains(&s)
}
}
#[derive(Debug)]
pub enum ControlCow {
Owned(Control),
Borrowed(&'static Control),
}
impl From<&'static Control> for ControlCow {
fn from(c: &'static Control) -> Self { Self::Borrowed(c) }
}
impl From<Control> for ControlCow {
fn from(c: Control) -> Self { Self::Owned(c) }
}
impl Deref for ControlCow {
type Target = Control;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(c) => c,
Self::Borrowed(c) => c,
}
}
}
impl Default for ControlCow {
fn default() -> Self { Self::Owned(Control::default()) }
}
impl ControlCow {
pub fn into_seq(self) -> VecDeque<Cmd> {
match self {
Self::Owned(c) => c.exec.into(),
Self::Borrowed(c) => c.to_seq(),
// TODO: remove this once Yazi 0.3 is released
impl<'de> Deserialize<'de> for Control {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
pub struct Shadow {
pub on: Vec<Key>,
pub run: Option<VecCmd>,
pub exec: Option<VecCmd>,
pub desc: Option<String>,
}
let shadow = Shadow::deserialize(deserializer)?;
#[derive(Deserialize)]
struct VecCmd(#[serde(deserialize_with = "super::run_deserialize")] Vec<Cmd>);
let Some(run) = shadow.run.or(shadow.exec) else {
return Err(serde::de::Error::custom("missing field `run` within `[keymap]`"));
};
Ok(Self { on: shadow.on, run: run.0, desc: shadow.desc })
}
}

View file

@ -0,0 +1,43 @@
use std::{collections::VecDeque, ops::Deref};
use yazi_shared::event::Cmd;
use super::Control;
#[derive(Debug)]
pub enum ControlCow {
Owned(Control),
Borrowed(&'static Control),
}
impl From<&'static Control> for ControlCow {
fn from(c: &'static Control) -> Self { Self::Borrowed(c) }
}
impl From<Control> for ControlCow {
fn from(c: Control) -> Self { Self::Owned(c) }
}
impl Deref for ControlCow {
type Target = Control;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(c) => c,
Self::Borrowed(c) => c,
}
}
}
impl Default for ControlCow {
fn default() -> Self { Self::Owned(Control::default()) }
}
impl ControlCow {
pub fn into_seq(self) -> VecDeque<Cmd> {
match self {
Self::Owned(c) => c.run.into(),
Self::Borrowed(c) => c.to_seq(),
}
}
}

View file

@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{fmt::{Display, Write}, str::FromStr};
use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
@ -132,22 +132,22 @@ impl TryFrom<String> for Key {
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
}
impl ToString for Key {
fn to_string(&self) -> String {
impl Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(c) = self.plain() {
let c = if self.shift { c.to_ascii_uppercase() } else { c };
return if c == ' ' { "<Space>".to_string() } else { c.to_string() };
return if c == ' ' { write!(f, "<Space>") } else { f.write_char(c) };
}
let mut s = "<".to_string();
write!(f, "<")?;
if self.ctrl {
s += "C-";
write!(f, "C-")?;
}
if self.alt {
s += "A-";
write!(f, "A-")?;
}
if self.shift && !matches!(self.code, KeyCode::Char(_)) {
s += "S-";
write!(f, "S-")?;
}
let code = match self.code {
@ -181,12 +181,12 @@ impl ToString for Key {
KeyCode::Char(' ') => "Space",
KeyCode::Char(c) => {
s.push(if self.shift { c.to_ascii_uppercase() } else { c });
f.write_char(if self.shift { c.to_ascii_uppercase() } else { c })?;
""
}
_ => "Unknown",
};
s + code + ">"
write!(f, "{}>", code)
}
}

View file

@ -1,10 +1,12 @@
mod control;
mod exec;
mod cow;
mod key;
mod keymap;
mod run;
pub use control::*;
#[allow(unused_imports)]
pub use exec::*;
pub use cow::*;
pub use key::*;
pub use keymap::*;
#[allow(unused_imports)]
pub use run::*;

View file

@ -4,16 +4,16 @@ use anyhow::{bail, Result};
use serde::{de::{self, Visitor}, Deserializer};
use yazi_shared::event::Cmd;
pub(super) fn exec_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Vec<Cmd>, D::Error>
where
D: Deserializer<'de>,
{
struct ExecVisitor;
struct RunVisitor;
fn parse(s: &str) -> Result<Cmd> {
let s = shell_words::split(s)?;
if s.is_empty() {
bail!("`exec` cannot be empty");
bail!("`run` cannot be empty");
}
let mut cmd = Cmd { name: s[0].clone(), ..Default::default() };
@ -30,11 +30,11 @@ where
Ok(cmd)
}
impl<'de> Visitor<'de> for ExecVisitor {
impl<'de> Visitor<'de> for RunVisitor {
type Value = Vec<Cmd>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a `exec` string or array of strings within [keymap]")
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>
@ -46,7 +46,7 @@ where
cmds.push(parse(value).map_err(de::Error::custom)?);
}
if cmds.is_empty() {
return Err(de::Error::custom("`exec` within [keymap] cannot be empty"));
return Err(de::Error::custom("`run` within keymap.toml cannot be empty"));
}
Ok(cmds)
}
@ -59,5 +59,5 @@ where
}
}
deserializer.deserialize_any(ExecVisitor)
deserializer.deserialize_any(RunVisitor)
}

View file

@ -1,6 +1,6 @@
#![allow(clippy::module_inception)]
use yazi_shared::RoCell;
use yazi_shared::{RoCell, Xdg};
pub mod keymap;
mod layout;
@ -23,11 +23,11 @@ pub(crate) use pattern::*;
pub(crate) use preset::*;
pub use priority::*;
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
static MERGED_YAZI: RoCell<String> = RoCell::new();
static MERGED_KEYMAP: RoCell<String> = RoCell::new();
static MERGED_THEME: RoCell<String> = RoCell::new();
static MERGED_YAZI: RoCell<String> = RoCell::new();
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
pub static LOG: RoCell<log::Log> = RoCell::new();
@ -42,12 +42,12 @@ pub static SELECT: RoCell<popup::Select> = RoCell::new();
pub static WHICH: RoCell<which::Which> = RoCell::new();
pub fn init() {
LAYOUT.with(Default::default);
let config_dir = yazi_shared::Xdg::config_dir().unwrap();
let config_dir = Xdg::config_dir();
MERGED_YAZI.init(Preset::yazi(&config_dir));
MERGED_KEYMAP.init(Preset::keymap(&config_dir));
MERGED_THEME.init(Preset::theme(&config_dir));
MERGED_YAZI.init(Preset::yazi(&config_dir));
LAYOUT.with(Default::default);
KEYMAP.with(Default::default);
LOG.with(Default::default);

View file

@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{fmt::Display, str::FromStr};
use anyhow::bail;
use serde::{Deserialize, Serialize};
@ -39,9 +39,9 @@ impl TryFrom<String> for SortBy {
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
}
impl ToString for SortBy {
fn to_string(&self) -> String {
match self {
impl Display for SortBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::None => "none",
Self::Modified => "modified",
Self::Created => "created",
@ -49,7 +49,6 @@ impl ToString for SortBy {
Self::Alphabetical => "alphabetical",
Self::Natural => "natural",
Self::Size => "size",
}
.to_string()
})
}
}

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer};
use yazi_shared::MIME_DIR;
use super::Opener;
use crate::{open::OpenRule, MERGED_YAZI};
use crate::{open::OpenRule, Preset, MERGED_YAZI};
#[derive(Debug)]
pub struct Open {
@ -70,15 +70,22 @@ impl<'de> Deserialize<'de> for Open {
}
#[derive(Deserialize)]
struct OuterOpen {
rules: Vec<OpenRule>,
rules: Vec<OpenRule>,
#[serde(default)]
prepend_rules: Vec<OpenRule>,
#[serde(default)]
append_rules: Vec<OpenRule>,
}
let outer = Outer::deserialize(deserializer)?;
let mut outer = Outer::deserialize(deserializer)?;
Preset::mix(&mut outer.open.rules, outer.open.prepend_rules, outer.open.append_rules);
let openers = outer
.opener
.into_iter()
.map(|(k, v)| (k, v.into_iter().filter_map(|o| o.take()).collect::<IndexSet<_>>()))
.collect();
Ok(Self { rules: outer.open.rules, openers })
}
}

View file

@ -2,7 +2,7 @@ use serde::{Deserialize, Deserializer};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Opener {
pub exec: String,
pub run: String,
pub block: bool,
pub orphan: bool,
pub desc: String,
@ -32,7 +32,9 @@ impl<'de> Deserialize<'de> for Opener {
{
#[derive(Deserialize)]
pub struct Shadow {
exec: String,
run: Option<String>,
// TODO: remove this once Yazi 0.3 is released --
exec: Option<String>,
#[serde(default)]
block: bool,
#[serde(default)]
@ -43,22 +45,18 @@ impl<'de> Deserialize<'de> for Opener {
}
let shadow = Shadow::deserialize(deserializer)?;
if shadow.exec.is_empty() {
return Err(serde::de::Error::custom("`exec` cannot be empty"));
// TODO: remove this once Yazi 0.3 is released --
let run = shadow.run.or(shadow.exec).unwrap_or_default();
// TODO: -- remove this once Yazi 0.3 is released
if run.is_empty() {
return Err(serde::de::Error::custom("`run` cannot be empty"));
}
let desc =
shadow.desc.unwrap_or_else(|| shadow.exec.split_whitespace().next().unwrap().to_string());
let desc = shadow.desc.unwrap_or_else(|| run.split_whitespace().next().unwrap().to_string());
let spread =
shadow.exec.contains("$@") || shadow.exec.contains("%*") || shadow.exec.contains("$*");
Ok(Self {
exec: shadow.exec,
block: shadow.block,
orphan: shadow.orphan,
desc,
for_: shadow.for_,
spread,
})
let spread = run.contains("$@") || run.contains("%*") || run.contains("$*");
Ok(Self { run, block: shadow.block, orphan: shadow.orphan, desc, for_: shadow.for_, spread })
}
}

View file

@ -1,10 +1,12 @@
mod exec;
mod plugin;
mod props;
mod rule;
mod run;
#[allow(unused_imports)]
pub use exec::*;
pub use plugin::*;
pub use props::*;
pub use rule::*;
#[allow(unused_imports)]
pub use run::*;
pub const MAX_PRELOADERS: u8 = 32;

View file

@ -1,9 +1,10 @@
use std::path::Path;
use serde::Deserialize;
use yazi_shared::{event::Cmd, Condition, MIME_DIR};
use yazi_shared::MIME_DIR;
use crate::{pattern::Pattern, plugin::MAX_PRELOADERS, Preset, Priority, MERGED_YAZI};
use super::PluginRule;
use crate::{plugin::MAX_PRELOADERS, Preset, MERGED_YAZI};
#[derive(Deserialize)]
pub struct Plugin {
@ -86,29 +87,3 @@ impl Plugin {
})
}
}
#[derive(Deserialize)]
pub struct PluginRule {
#[serde(default)]
pub id: u8,
pub cond: Option<Condition>,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
#[serde(rename = "exec")]
#[serde(deserialize_with = "super::exec_deserialize")]
pub cmd: Cmd,
#[serde(default)]
pub sync: bool,
#[serde(default)]
pub multi: bool,
#[serde(default)]
pub prio: Priority,
}
impl PluginRule {
#[inline]
fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }
#[inline]
fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
}

View file

@ -0,0 +1,68 @@
use serde::{Deserialize, Deserializer};
use yazi_shared::{event::Cmd, Condition};
use crate::{Pattern, Priority};
pub struct PluginRule {
pub id: u8,
pub cond: Option<Condition>,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub cmd: Cmd,
pub sync: bool,
pub multi: bool,
pub prio: Priority,
}
impl PluginRule {
#[inline]
pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }
#[inline]
pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
}
// TODO: remove this once Yazi 0.3 is released
impl<'de> Deserialize<'de> for PluginRule {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
pub struct Shadow {
#[serde(default)]
pub id: u8,
pub cond: Option<Condition>,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub run: Option<WrappedCmd>,
pub exec: Option<WrappedCmd>,
#[serde(default)]
pub sync: bool,
#[serde(default)]
pub multi: bool,
#[serde(default)]
pub prio: Priority,
}
let shadow = Shadow::deserialize(deserializer)?;
#[derive(Deserialize)]
struct WrappedCmd(#[serde(deserialize_with = "super::run_deserialize")] Cmd);
let Some(run) = shadow.run.or(shadow.exec) else {
return Err(serde::de::Error::custom("missing field `run` within `[plugin]`"));
};
Ok(Self {
id: shadow.id,
cond: shadow.cond,
name: shadow.name,
mime: shadow.mime,
cmd: run.0,
sync: shadow.sync,
multi: shadow.multi,
prio: shadow.prio,
})
}
}

View file

@ -4,24 +4,24 @@ use anyhow::Result;
use serde::{de::{self, Visitor}, Deserializer};
use yazi_shared::event::Cmd;
pub(super) fn exec_deserialize<'de, D>(deserializer: D) -> Result<Cmd, D::Error>
pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result<Cmd, D::Error>
where
D: Deserializer<'de>,
{
struct ExecVisitor;
struct RunVisitor;
impl<'de> Visitor<'de> for ExecVisitor {
impl<'de> Visitor<'de> for RunVisitor {
type Value = Cmd;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a `exec` string or array of strings")
formatter.write_str("a `run` string or array of strings")
}
fn visit_seq<A>(self, _: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
Err(de::Error::custom("`exec` within [plugin] must be a string"))
Err(de::Error::custom("`run` within [plugin] must be a string"))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
@ -29,11 +29,11 @@ where
E: de::Error,
{
if value.is_empty() {
return Err(de::Error::custom("`exec` within [plugin] cannot be empty"));
return Err(de::Error::custom("`run` within [plugin] cannot be empty"));
}
Ok(Cmd { name: value.to_owned(), ..Default::default() })
}
}
deserializer.deserialize_any(ExecVisitor)
deserializer.deserialize_any(RunVisitor)
}

View file

@ -1,24 +1,63 @@
use std::{fmt::Display, str::FromStr};
use anyhow::bail;
use serde::Deserialize;
#[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq)]
#[serde(try_from = "String")]
pub enum Origin {
#[default]
#[serde(rename = "top-left")]
TopLeft,
#[serde(rename = "top-center")]
TopCenter,
#[serde(rename = "top-right")]
TopRight,
#[serde(rename = "bottom-left")]
BottomLeft,
#[serde(rename = "bottom-center")]
BottomCenter,
#[serde(rename = "bottom-right")]
BottomRight,
#[serde(rename = "center")]
Center,
#[serde(rename = "hovered")]
Hovered,
}
impl FromStr for Origin {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"top-left" => Self::TopLeft,
"top-center" => Self::TopCenter,
"top-right" => Self::TopRight,
"bottom-left" => Self::BottomLeft,
"bottom-center" => Self::BottomCenter,
"bottom-right" => Self::BottomRight,
"center" => Self::Center,
"hovered" => Self::Hovered,
_ => bail!("Invalid `origin` value: {s}"),
})
}
}
impl TryFrom<String> for Origin {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> { Self::from_str(&value) }
}
impl Display for Origin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::TopLeft => "top-left",
Self::TopCenter => "top-center",
Self::TopRight => "top-right",
Self::BottomLeft => "bottom-left",
Self::BottomCenter => "bottom-center",
Self::BottomRight => "bottom-right",
Self::Center => "center",
Self::Hovered => "hovered",
})
}
}

View file

@ -1,23 +1,35 @@
use std::{mem, path::{Path, PathBuf}};
use anyhow::Context;
use toml::{Table, Value};
use crate::theme::Flavor;
pub(crate) struct Preset;
impl Preset {
#[inline]
pub(crate) fn keymap(dir: &Path) -> String {
Self::merge_str(dir.join("keymap.toml"), include_str!("../preset/keymap.toml"))
pub(crate) fn yazi(p: &Path) -> String {
Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml"))
}
#[inline]
pub(crate) fn theme(dir: &Path) -> String {
Self::merge_str(dir.join("theme.toml"), include_str!("../preset/theme.toml"))
pub(crate) fn keymap(p: &Path) -> String {
Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml"))
}
#[inline]
pub(crate) fn yazi(dir: &Path) -> String {
Self::merge_str(dir.join("yazi.toml"), include_str!("../preset/yazi.toml"))
pub(crate) fn theme(p: &Path) -> String {
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
return include_str!("../preset/theme.toml").to_owned();
};
let Some(use_) = Flavor::parse_use(&user) else {
return Self::merge_str(&user, include_str!("../preset/theme.toml"));
};
let p = p.join(format!("flavors/{}.yazi/flavor.toml", use_));
let flavor = std::fs::read_to_string(&p)
.with_context(|| format!("Failed to load flavor {:?}", p))
.unwrap();
Self::merge_str(&user, &Self::merge_str(&flavor, include_str!("../preset/theme.toml")))
}
#[inline]
@ -25,6 +37,24 @@ impl Preset {
*a = b.into_iter().chain(mem::take(a)).chain(c).collect();
}
#[inline]
pub(crate) fn merge_str(user: &str, base: &str) -> String {
let mut t = user.parse().unwrap();
Self::merge(&mut t, base.parse().unwrap(), 2);
t.to_string()
}
#[inline]
fn merge_path(user: PathBuf, base: &str) -> String {
let s = std::fs::read_to_string(user).unwrap_or_default();
if s.is_empty() {
return base.to_string();
}
Self::merge_str(&s, base)
}
fn merge(a: &mut Table, b: Table, max: u8) {
for (k, v) in b {
let Some(a) = a.get_mut(&k) else {
@ -45,12 +75,4 @@ impl Preset {
*a = v;
}
}
fn merge_str(user: PathBuf, base: &str) -> String {
let mut user = std::fs::read_to_string(user).unwrap_or_default().parse::<Table>().unwrap();
let base = base.parse::<Table>().unwrap();
Self::merge(&mut user, base, 2);
user.to_string()
}
}

View file

@ -2,9 +2,9 @@ use std::{fs, path::PathBuf, time::{self, SystemTime}};
use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::{fs::expand_path, Xdg};
use yazi_shared::fs::expand_path;
use crate::{validation::check_validation, MERGED_YAZI};
use crate::{validation::check_validation, Xdg, MERGED_YAZI};
#[derive(Debug, Serialize)]
pub struct Preview {

View file

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub struct Flavor {
#[serde(rename = "use")]
pub use_: String,
}
impl Flavor {
pub fn parse_use(s: &str) -> Option<String> {
#[derive(Deserialize)]
struct Outer {
flavor: Inner,
}
#[derive(Deserialize)]
struct Inner {
#[serde(rename = "use")]
pub use_: String,
}
toml::from_str::<Outer>(s).ok().map(|o| o.flavor.use_).filter(|s| !s.is_empty())
}
}

View file

@ -1,5 +1,6 @@
mod color;
mod filetype;
mod flavor;
mod icon;
mod is;
mod style;
@ -7,6 +8,7 @@ mod theme;
pub use color::*;
pub use filetype::*;
pub use flavor::*;
pub use icon::*;
pub use is::*;
pub use style::*;

View file

@ -2,11 +2,49 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::fs::expand_path;
use yazi_shared::{fs::expand_path, Xdg};
use super::{Filetype, Icon, Style};
use super::{Filetype, Flavor, Icon, Style};
use crate::{validation::check_validation, MERGED_THEME};
#[derive(Deserialize, Serialize)]
pub struct Theme {
pub flavor: Flavor,
pub manager: Manager,
status: Status,
pub input: Input,
pub select: Select,
pub completion: Completion,
pub tasks: Tasks,
pub which: Which,
pub help: Help,
pub notify: Notify,
// File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
pub filetypes: Vec<Filetype>,
#[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
pub icons: Vec<Icon>,
}
impl Default for Theme {
fn default() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
check_validation(theme.manager.validate());
check_validation(theme.which.validate());
if theme.flavor.use_.is_empty() {
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
} else {
theme.manager.syntect_theme =
Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_));
}
theme
}
}
#[derive(Deserialize, Serialize, Validate)]
pub struct Manager {
cwd: Style,
@ -118,7 +156,7 @@ pub struct Which {
#[derive(Deserialize, Serialize)]
pub struct Help {
pub on: Style,
pub exec: Style,
pub run: Style,
pub desc: Style,
pub hovered: Style,
@ -126,32 +164,12 @@ pub struct Help {
}
#[derive(Deserialize, Serialize)]
pub struct Theme {
pub manager: Manager,
status: Status,
pub input: Input,
pub select: Select,
pub completion: Completion,
pub tasks: Tasks,
pub which: Which,
pub help: Help,
pub struct Notify {
pub title_info: Style,
pub title_warn: Style,
pub title_error: Style,
// File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
pub filetypes: Vec<Filetype>,
#[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
pub icons: Vec<Icon>,
}
impl Default for Theme {
fn default() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
check_validation(theme.manager.validate());
check_validation(theme.which.validate());
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
theme
}
pub icon_info: String,
pub icon_warn: String,
pub icon_error: String,
}

View file

@ -20,7 +20,7 @@ impl FromStr for SortBy {
"none" => Self::None,
"key" => Self::Key,
"desc" => Self::Desc,
_ => bail!("Invalid sort option: {s}"),
_ => bail!("Invalid `sort_by` value: {s}"),
})
}
}

View file

@ -13,6 +13,7 @@ yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" }
yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }

View file

@ -1,6 +1,7 @@
use yazi_shared::{emit, event::Cmd, render, Layer};
use yazi_proxy::InputProxy;
use yazi_shared::{event::Cmd, render};
use crate::{completion::Completion, input::Input};
use crate::completion::Completion;
pub struct Opt {
submit: bool,
@ -11,16 +12,11 @@ impl From<Cmd> for Opt {
}
impl Completion {
#[inline]
pub fn _close() {
emit!(Call(Cmd::new("close"), Layer::Completion));
}
pub fn close(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if let Some(s) = self.selected().filter(|_| opt.submit) {
Input::_complete(s, self.ticket);
InputProxy::complete(s, self.ticket);
}
self.caches.clear();

View file

@ -20,14 +20,6 @@ impl From<Cmd> for Opt {
}
impl Completion {
#[inline]
pub fn _trigger(word: &str, ticket: usize) {
emit!(Call(
Cmd::args("trigger", vec![word.to_owned()]).with("ticket", ticket),
Layer::Completion
));
}
pub fn trigger(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if opt.ticket < self.ticket {

View file

@ -2,10 +2,11 @@ use std::{mem, time::SystemTime};
use ratatui::layout::Rect;
use yazi_config::{LAYOUT, MANAGER};
use yazi_proxy::ManagerProxy;
use yazi_shared::fs::{File, FilesOp, Url};
use super::FolderStage;
use crate::{folder::Files, manager::Manager, Step};
use crate::{folder::Files, Step};
#[derive(Default)]
pub struct Folder {
@ -99,7 +100,7 @@ impl Folder {
let new = self.cursor / limit;
if mem::replace(&mut self.page, new) != new || force {
Manager::_update_paged_by(new, &self.cwd);
ManagerProxy::update_paged_by(new, &self.cwd);
}
}

View file

@ -1,6 +1,7 @@
use yazi_proxy::CompletionProxy;
use yazi_shared::{event::Cmd, render, InputError};
use crate::{completion::Completion, input::Input};
use crate::input::Input;
pub struct Opt {
submit: bool,
@ -18,7 +19,7 @@ impl Input {
let opt = opt.into() as Opt;
if self.completion {
Completion::_close();
CompletionProxy::close();
}
if let Some(cb) = self.callback.take() {

View file

@ -1,6 +1,6 @@
use std::path::MAIN_SEPARATOR;
use yazi_shared::{emit, event::Cmd, render, Layer};
use yazi_shared::{event::Cmd, render};
use crate::input::Input;
@ -19,11 +19,6 @@ impl From<Cmd> for Opt {
}
impl Input {
#[inline]
pub fn _complete(word: &str, ticket: usize) {
emit!(Call(Cmd::args("complete", vec![word.to_owned()]).with("ticket", ticket), Layer::Input));
}
pub fn complete(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if self.ticket != opt.ticket {

View file

@ -1,6 +1,7 @@
use yazi_proxy::CompletionProxy;
use yazi_shared::{event::Cmd, render};
use crate::{completion::Completion, input::{op::InputOp, Input, InputMode}};
use crate::input::{op::InputOp, Input, InputMode};
pub struct Opt;
@ -26,7 +27,7 @@ impl Input {
self.move_(-1);
if self.completion {
Completion::_close();
CompletionProxy::close();
}
}
}

View file

@ -1,28 +1,10 @@
use tokio::sync::mpsc;
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, render, InputError, Layer};
use yazi_proxy::options::InputOpt;
use yazi_shared::render;
use crate::input::Input;
pub struct Opt {
cfg: InputCfg,
tx: mpsc::UnboundedSender<Result<String, InputError>>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Input {
pub fn _show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> {
let (tx, rx) = mpsc::unbounded_channel();
emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Input));
rx
}
pub fn show(&mut self, opt: impl TryInto<Opt>) {
pub fn show(&mut self, opt: impl TryInto<InputOpt>) {
let Ok(opt) = opt.try_into() else {
return;
};

View file

@ -22,8 +22,4 @@ pub mod which;
pub use clipboard::*;
pub use step::*;
pub fn init() {
CLIPBOARD.with(Default::default);
yazi_scheduler::init();
}
pub fn init() { CLIPBOARD.with(Default::default); }

View file

@ -1,10 +1,11 @@
use std::path::{PathBuf, MAIN_SEPARATOR};
use std::path::PathBuf;
use tokio::fs;
use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{event::Cmd, fs::{File, FilesOp, Url}};
use crate::{input::Input, manager::Manager};
use crate::manager::Manager;
pub struct Opt {
force: bool,
@ -19,20 +20,20 @@ impl Manager {
let opt = opt.into() as Opt;
let cwd = self.cwd().to_owned();
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::create());
let mut result = InputProxy::show(InputCfg::create());
let Some(Ok(name)) = result.recv().await else {
return Ok(());
};
let path = cwd.join(&name);
if !opt.force && fs::symlink_metadata(&path).await.is_ok() {
match Input::_show(InputCfg::overwrite()).recv().await {
match InputProxy::show(InputCfg::overwrite()).recv().await {
Some(Ok(c)) if c == "y" || c == "Y" => (),
_ => return Ok(()),
}
}
if name.ends_with(MAIN_SEPARATOR) {
if name.ends_with('/') || name.ends_with('\\') {
fs::create_dir_all(&path).await?;
} else {
fs::create_dir_all(&path.parent().unwrap()).await.ok();
@ -43,7 +44,7 @@ impl Manager {
Url::from(path.components().take(cwd.components().count() + 1).collect::<PathBuf>());
if let Ok(f) = File::from(child.clone()).await {
FilesOp::Creating(cwd, vec![f]).emit();
Manager::_hover(Some(child));
ManagerProxy::hover(Some(child));
}
Ok::<(), anyhow::Error>(())
});

View file

@ -1,6 +1,6 @@
use std::collections::BTreeSet;
use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer};
use yazi_shared::{event::Cmd, fs::Url, render};
use crate::manager::Manager;
@ -16,14 +16,6 @@ impl From<Option<Url>> for Opt {
}
impl Manager {
#[inline]
pub fn _hover(url: Option<Url>) {
emit!(Call(
Cmd::args("hover", url.map_or_else(Vec::new, |u| vec![u.to_string()])),
Layer::Manager
));
}
pub fn hover(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;

View file

@ -4,20 +4,19 @@ use tracing::error;
use yazi_boot::ARGS;
use yazi_config::{popup::SelectCfg, OPEN};
use yazi_plugin::isolate;
use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, Layer, MIME_DIR};
use yazi_proxy::{options::OpenDoOpt, ManagerProxy, TasksProxy};
use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, MIME_DIR};
use crate::{folder::Folder, manager::Manager, select::Select, tasks::Tasks};
use crate::{folder::Folder, manager::Manager, tasks::Tasks};
pub struct Opt {
targets: Vec<(Url, String)>,
interactive: bool,
hovered: bool,
}
impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self {
fn from(c: Cmd) -> Self {
Self {
targets: c.take_data().unwrap_or_default(),
interactive: c.named.contains_key("interactive"),
hovered: c.named.contains_key("hovered"),
}
@ -29,23 +28,19 @@ impl Manager {
if !self.active_mut().try_escape_visual() {
return;
}
let mut opt = opt.into() as Opt;
let selected = if opt.hovered {
self.hovered().map(|h| vec![&h.url]).unwrap_or_default()
} else {
self.selected_or_hovered()
let Some(hovered) = self.hovered().map(|h| h.url()) else {
return;
};
if selected.is_empty() {
return;
} else if Self::quit_with_selected(&selected) {
let opt = opt.into() as Opt;
let selected = if opt.hovered { vec![&hovered] } else { self.selected_or_hovered() };
if Self::quit_with_selected(&selected) {
return;
}
let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]);
for u in selected {
if self.mimetype.get(u).is_some() {
if self.mimetype.contains_key(u) {
done.push((u.clone(), String::new()));
} else if self.guess_folder(u) {
done.push((u.clone(), MIME_DIR.to_owned()));
@ -55,8 +50,8 @@ impl Manager {
}
if todo.is_empty() {
opt.targets = done;
return self.open_do(opt, tasks);
return self
.open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }, tasks);
}
tokio::spawn(async move {
@ -69,27 +64,15 @@ impl Manager {
done.extend(files.iter().map(|f| (f.url(), String::new())));
if let Err(e) = isolate::preload("mime", files, true).await {
error!("preload in watcher failed: {e}");
error!("preload in open failed: {e}");
}
Self::_open_do(done, opt.interactive);
ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive });
});
}
#[inline]
pub fn _open_do(targets: Vec<(Url, String)>, interactive: bool) {
emit!(Call(
Cmd::new("open_do").with_bool("interactive", interactive).with_data(targets),
Layer::Manager
));
}
pub fn open_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let opt = opt.into() as Opt;
if opt.targets.is_empty() {
return;
}
pub fn open_do(&mut self, opt: impl Into<OpenDoOpt>, tasks: &Tasks) {
let opt = opt.into() as OpenDoOpt;
let targets: Vec<_> = opt
.targets
.into_iter()
@ -101,8 +84,7 @@ impl Manager {
if targets.is_empty() {
return;
} else if !opt.interactive {
tasks.file_open(&targets);
return;
return tasks.file_open(&opt.hovered, &targets);
}
let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect();
@ -110,11 +92,13 @@ impl Manager {
return;
}
let urls = targets.into_iter().map(|(u, _)| u).collect();
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
tokio::spawn(async move {
let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect()));
let result = yazi_proxy::SelectProxy::show(SelectCfg::open(
openers.iter().map(|o| o.desc.clone()).collect(),
));
if let Ok(choice) = result.await {
Tasks::_open(urls, openers[choice].clone());
TasksProxy::open_with(urls, openers[choice].clone());
}
});
}

View file

@ -1,4 +1,4 @@
use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer};
use yazi_shared::{event::Cmd, fs::Url, render};
use crate::manager::Manager;
@ -25,11 +25,6 @@ impl From<bool> for Opt {
}
impl Manager {
#[inline]
pub fn _peek(force: bool) {
emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Manager));
}
pub fn peek(&mut self, opt: impl Into<Opt>) {
let Some(hovered) = self.hovered().cloned() else {
return render!(self.active_mut().preview.reset());

View file

@ -1,7 +1,8 @@
use yazi_config::popup::InputCfg;
use yazi_proxy::InputProxy;
use yazi_shared::{emit, event::{Cmd, EventQuit}};
use crate::{input::Input, manager::Manager, tasks::Tasks};
use crate::{manager::Manager, tasks::Tasks};
#[derive(Default)]
pub struct Opt {
@ -25,7 +26,7 @@ impl Manager {
}
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::quit(tasks));
let mut result = InputProxy::show(InputCfg::quit(tasks));
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
emit!(Quit(opt));

View file

@ -1,15 +1,10 @@
use std::env;
use yazi_shared::{emit, event::Cmd, Layer};
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tasks::Tasks};
impl Manager {
#[inline]
pub fn _refresh() {
emit!(Call(Cmd::new("refresh"), Layer::Manager));
}
pub fn refresh(&mut self, _: Cmd, tasks: &Tasks) {
env::set_current_dir(self.cwd()).ok();
env::set_var("PWD", self.cwd());

View file

@ -1,7 +1,8 @@
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{event::Cmd, fs::Url};
use crate::{input::Input, manager::Manager, tasks::Tasks};
use crate::{manager::Manager, tasks::Tasks};
pub struct Opt {
force: bool,
@ -33,7 +34,7 @@ impl Manager {
}
tokio::spawn(async move {
let mut result = Input::_show(if opt.permanently {
let mut result = InputProxy::show(if opt.permanently {
InputCfg::delete(opt.targets.len())
} else {
InputCfg::trash(opt.targets.len())
@ -44,19 +45,11 @@ impl Manager {
return;
}
Self::_remove_do(opt.targets, opt.permanently);
ManagerProxy::remove_do(opt.targets, opt.permanently);
}
});
}
#[inline]
pub fn _remove_do(targets: Vec<Url>, permanently: bool) {
emit!(Call(
Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets),
Layer::Manager
));
}
pub fn remove_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let opt = opt.into() as Opt;
for u in &opt.targets {

View file

@ -1,13 +1,14 @@
use std::{collections::BTreeMap, ffi::OsStr, io::{stdout, BufWriter, Write}, path::PathBuf};
use std::{collections::BTreeMap, ffi::{OsStr, OsString}, io::{stdout, BufWriter, Write}, path::PathBuf};
use anyhow::{anyhow, bail, Result};
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
use yazi_config::{popup::InputCfg, OPEN, PREVIEW};
use yazi_plugin::external::{self, ShellOpt};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::{AppProxy, InputProxy, ManagerProxy};
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, fs::{max_common_root, File, FilesOp, Url}, term::Term, Defer};
use crate::{input::Input, manager::Manager};
use crate::manager::Manager;
pub struct Opt {
force: bool,
@ -49,7 +50,7 @@ impl Manager {
let file = File::from(new.clone()).await?;
FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit();
FilesOp::Upserting(file.parent().unwrap(), BTreeMap::from_iter([(old, file)])).emit();
Ok(Self::_hover(Some(new)))
Ok(ManagerProxy::hover(Some(new)))
}
pub fn rename(&mut self, opt: impl Into<Opt>) {
@ -77,7 +78,7 @@ impl Manager {
};
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::rename().with_value(name).with_cursor(cursor));
let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let Some(Ok(name)) = result.recv().await else {
return;
};
@ -88,7 +89,7 @@ impl Manager {
return;
}
let mut result = Input::_show(InputCfg::overwrite());
let mut result = InputProxy::show(InputCfg::overwrite());
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
Self::rename_and_hover(hovered, Url::from(new)).await.ok();
@ -122,14 +123,14 @@ impl Manager {
let _guard = BLOCKER.acquire().await.unwrap();
let _defer = Defer::new(|| {
Scheduler::app_resume();
AppProxy::resume();
tokio::spawn(fs::remove_file(tmp.clone()))
});
Scheduler::app_stop().await;
AppProxy::stop().await;
let mut child = external::shell(ShellOpt {
cmd: (*opener.exec).into(),
args: vec![tmp.to_owned().into()],
cmd: (*opener.run).into(),
args: vec![OsString::new(), tmp.to_owned().into()],
piped: false,
orphan: false,
})?;

View file

@ -1,4 +1,4 @@
use yazi_scheduler::Scheduler;
use yazi_proxy::AppProxy;
use yazi_shared::event::Cmd;
use crate::manager::Manager;
@ -7,7 +7,7 @@ impl Manager {
pub fn suspend(&mut self, _: Cmd) {
#[cfg(unix)]
tokio::spawn(async move {
Scheduler::app_stop().await;
AppProxy::stop().await;
unsafe { libc::raise(libc::SIGTSTP) };
});
}

View file

@ -1,5 +1,6 @@
use std::borrow::Cow;
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, fs::FilesOp, render};
use crate::{folder::Folder, manager::Manager, tab::Tab, tasks::Tasks};
@ -56,8 +57,8 @@ impl Manager {
return;
}
Self::_hover(None); // Re-hover in next loop
Self::_update_paged(); // Update for paged files in next loop
ManagerProxy::hover(None); // Re-hover in next loop
ManagerProxy::update_paged(); // Update for paged files in next loop
if calc {
tasks.preload_sorted(&tab.current.files);
}
@ -73,7 +74,7 @@ impl Manager {
}
if !foreign {
Self::_peek(true);
ManagerProxy::peek(true);
}
}

View file

@ -1,4 +1,4 @@
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use yazi_shared::{event::Cmd, fs::Url};
use crate::{manager::Manager, tasks::Tasks};
@ -22,19 +22,6 @@ impl From<()> for Opt {
}
impl Manager {
#[inline]
pub fn _update_paged() {
emit!(Call(Cmd::new("update_paged"), Layer::Manager));
}
#[inline]
pub fn _update_paged_by(page: usize, only_if: &Url) {
emit!(Call(
Cmd::args("update_paged", vec![page.to_string()]).with("only-if", only_if.to_string()),
Layer::Manager
));
}
pub fn update_paged(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
let Ok(opt) = opt.try_into() else {
return;

View file

@ -52,4 +52,7 @@ impl Manager {
#[inline]
pub fn selected_or_hovered(&self) -> Vec<&Url> { self.tabs.active().selected_or_hovered() }
#[inline]
pub fn hovered_and_selected(&self) -> Vec<&Url> { self.tabs.active().hovered_and_selected() }
}

View file

@ -1,9 +1,10 @@
use std::ops::{Deref, DerefMut};
use yazi_boot::BOOT;
use yazi_proxy::ManagerProxy;
use yazi_shared::fs::Url;
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Tabs {
pub idx: usize,
@ -17,7 +18,7 @@ impl Tabs {
tabs.items[0].reveal(Url::from(BOOT.cwd.join(file)));
}
Manager::_refresh();
ManagerProxy::refresh();
tabs
}
@ -42,8 +43,8 @@ impl Tabs {
}
self.idx = idx;
Manager::_refresh();
Manager::_peek(true);
ManagerProxy::refresh();
ManagerProxy::peek(true);
}
}

View file

@ -5,22 +5,8 @@ use yazi_shared::{emit, event::Cmd, Layer};
use crate::notify::{Message, Notify};
impl Notify {
#[inline]
pub fn _push_warn(title: &str, content: &str) {
emit!(Call(
Cmd::new("notify")
.with("title", title)
.with("content", content)
.with("level", "warn")
.with("timeout", 5),
Layer::App
));
}
pub fn push(&mut self, msg: impl TryInto<Message>) {
let Ok(mut msg) = msg.try_into() else {
return;
};
pub fn push(&mut self, msg: impl Into<Message>) {
let mut msg = msg.into() as Message;
let instant = Instant::now();
msg.timeout += instant - self.messages.first().map_or(instant, |m| m.instant);

View file

@ -1,20 +0,0 @@
use std::str::FromStr;
pub enum Level {
Info,
Warn,
Error,
}
impl FromStr for Level {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"info" => Self::Info,
"warn" => Self::Warn,
"error" => Self::Error,
_ => return Err(()),
})
}
}

View file

@ -1,41 +1,31 @@
use std::time::{Duration, Instant};
use unicode_width::UnicodeWidthStr;
use yazi_shared::event::Cmd;
use yazi_proxy::options::{NotifyLevel, NotifyOpt};
use super::{Level, NOTIFY_BORDER};
use super::NOTIFY_BORDER;
pub struct Message {
pub title: String,
pub content: String,
pub level: Level,
pub instant: Instant,
pub level: NotifyLevel,
pub timeout: Duration,
pub instant: Instant,
pub percent: u8,
}
impl TryFrom<Cmd> for Message {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
let timeout = c.take_name("timeout").and_then(|s| s.parse::<f64>().ok()).ok_or(())?;
if timeout < 0.0 {
return Err(());
}
let content = c.take_name("content").ok_or(())?;
Ok(Self {
title: c.take_name("title").ok_or(())?,
content,
level: c.take_name("level").ok_or(())?.parse()?,
impl From<NotifyOpt> for Message {
fn from(opt: NotifyOpt) -> Self {
Self {
title: opt.title,
content: opt.content,
level: opt.level,
timeout: opt.timeout,
instant: Instant::now(),
timeout: Duration::from_secs_f64(timeout),
percent: 0,
})
}
}
}

View file

@ -1,9 +1,7 @@
mod commands;
mod level;
mod message;
mod notify;
pub use level::*;
pub use message::*;
pub use notify::*;

View file

@ -1,29 +1,10 @@
use anyhow::Result;
use tokio::sync::oneshot;
use yazi_config::popup::SelectCfg;
use yazi_shared::{emit, event::Cmd, render, term::Term, Layer};
use yazi_proxy::options::SelectOpt;
use yazi_shared::render;
use crate::select::Select;
pub struct Opt {
cfg: SelectCfg,
tx: oneshot::Sender<Result<usize>>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Select {
pub async fn _show(cfg: SelectCfg) -> Result<usize> {
let (tx, rx) = oneshot::channel();
emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Select));
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
}
pub fn show(&mut self, opt: impl TryInto<Opt>) {
pub fn show(&mut self, opt: impl TryInto<SelectOpt>) {
let Ok(opt) = opt.try_into() else {
return;
};

View file

@ -1,6 +1,7 @@
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, render};
use crate::{manager::Manager, tab::Tab, Step};
use crate::{tab::Tab, Step};
pub struct Opt {
step: Step,
@ -36,7 +37,7 @@ impl Tab {
}
}
Manager::_hover(None);
ManagerProxy::hover(None);
render!();
}
}

View file

@ -3,9 +3,10 @@ use std::{mem, time::Duration};
use tokio::{fs, pin};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError, Layer};
use yazi_proxy::{CompletionProxy, InputProxy, ManagerProxy, TabProxy};
use yazi_shared::{event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError};
use crate::{completion::Completion, input::Input, manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Opt {
target: Url,
@ -27,11 +28,6 @@ impl From<Url> for Opt {
}
impl Tab {
#[inline]
pub fn _cd(target: &Url) {
emit!(Call(Cmd::args("cd", vec![target.to_string()]), Layer::Manager));
}
pub fn cd(&mut self, opt: impl Into<Opt>) {
if !self.try_escape_visual() {
return;
@ -68,13 +64,13 @@ impl Tab {
self.backstack.push(opt.target.clone());
}
Manager::_refresh();
ManagerProxy::refresh();
render!();
}
fn cd_interactive(&mut self) {
tokio::spawn(async move {
let rx = Input::_show(InputCfg::cd());
let rx = InputProxy::show(InputCfg::cd());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);
@ -88,13 +84,13 @@ impl Tab {
};
if meta.is_dir() {
Tab::_cd(&u);
TabProxy::cd(&u);
} else {
Tab::_reveal(&u);
TabProxy::reveal(&u);
}
}
Err(InputError::Completed(before, ticket)) => {
Completion::_trigger(&before, ticket);
CompletionProxy::trigger(&before, ticket);
}
_ => break,
}

View file

@ -1,7 +1,8 @@
use bitflags::bitflags;
use yazi_proxy::{AppProxy, ManagerProxy};
use yazi_shared::{event::Cmd, render, render_and};
use crate::{manager::Manager, notify::Notify, tab::Tab};
use crate::tab::Tab;
bitflags! {
pub struct Opt: u8 {
@ -74,7 +75,7 @@ impl Tab {
self.selected.clear();
if self.current.hovered().is_some_and(|h| h.is_dir()) {
Manager::_peek(true);
ManagerProxy::peek(true);
}
render_and!(true)
}
@ -98,28 +99,26 @@ impl Tab {
}
pub fn try_escape_visual(&mut self) -> bool {
let state = self.mode.is_select();
let select = self.mode.is_select();
let Some((_, indices)) = self.mode.take_visual() else {
return true;
};
let mut success = true;
for f in indices.iter().filter_map(|i| self.current.files.get(*i)) {
if state {
success &= self.selected.add(&f.url);
} else {
self.selected.remove(&f.url);
}
}
render!();
let urls: Vec<_> =
indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| &f.url).collect();
if !success {
Notify::_push_warn(
let same = !self.current.cwd.is_search();
if !select {
self.selected.remove_many(&urls, same);
} else if self.selected.add_many(&urls, same) != urls.len() {
AppProxy::notify_warn(
"Escape visual mode",
"Some files cannot be selected, due to path nesting conflict.",
);
return false;
}
render!();
success
true
}
}

View file

@ -3,9 +3,10 @@ use std::time::Duration;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer};
use crate::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab};
use crate::{folder::{Filter, FilterCase}, tab::Tab};
#[derive(Default)]
pub struct Opt {
@ -28,7 +29,7 @@ impl Tab {
pub fn filter(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
tokio::spawn(async move {
let rx = Input::_show(InputCfg::filter());
let rx = InputProxy::show(InputCfg::filter());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);
@ -62,7 +63,7 @@ impl Tab {
};
if opt.done {
Manager::_update_paged(); // Update for paged files in next loop
ManagerProxy::update_paged(); // Update for paged files in next loop
}
let hovered = self.current.hovered().map(|f| f.url());
@ -71,7 +72,7 @@ impl Tab {
}
if self.current.repos(hovered) {
Manager::_hover(None);
ManagerProxy::hover(None);
}
render!();
}

View file

@ -3,9 +3,10 @@ use std::time::Duration;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_proxy::InputProxy;
use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer};
use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}};
use crate::{folder::FilterCase, tab::{Finder, Tab}};
pub struct Opt {
query: Option<String>,
@ -35,7 +36,7 @@ impl Tab {
pub fn find(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
tokio::spawn(async move {
let rx = Input::_show(InputCfg::find(opt.prev));
let rx = InputProxy::show(InputCfg::find(opt.prev));
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);

View file

@ -1,6 +1,7 @@
use yazi_proxy::ManagerProxy;
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
impl Tab {
pub fn hidden(&mut self, c: Cmd) {
@ -14,10 +15,10 @@ impl Tab {
self.apply_files_attrs();
if hovered.as_ref() != self.current.hovered().map(|f| &f.url) {
Manager::_hover(hovered);
ManagerProxy::hover(hovered);
} else if self.current.hovered().is_some_and(|f| f.is_dir()) {
Manager::_peek(true);
ManagerProxy::peek(true);
}
Manager::_update_paged();
ManagerProxy::update_paged();
}
}

View file

@ -1,5 +1,6 @@
use yazi_plugin::external::{self, FzfOpt, ZoxideOpt};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::{AppProxy, TabProxy};
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, fs::ends_with_slash, Defer};
use crate::tab::Tab;
@ -37,8 +38,8 @@ impl Tab {
let cwd = self.current.cwd.clone();
tokio::spawn(async move {
let _guard = BLOCKER.acquire().await.unwrap();
let _defer = Defer::new(Scheduler::app_resume);
Scheduler::app_stop().await;
let _defer = Defer::new(AppProxy::resume);
AppProxy::stop().await;
let result = if opt.type_ == OptType::Fzf {
external::fzf(FzfOpt { cwd }).await
@ -51,9 +52,9 @@ impl Tab {
};
if opt.type_ == OptType::Fzf && !ends_with_slash(&url) {
Tab::_reveal(&url)
TabProxy::reveal(&url)
} else {
Tab::_cd(&url)
TabProxy::cd(&url)
}
});
}

View file

@ -1,6 +1,7 @@
use yazi_shared::{emit, event::Cmd, fs::{expand_path, File, FilesOp, Url}, Layer};
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, fs::{expand_path, File, FilesOp, Url}};
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Opt {
target: Url,
@ -21,11 +22,6 @@ impl From<Url> for Opt {
}
impl Tab {
#[inline]
pub fn _reveal(target: &Url) {
emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager));
}
pub fn reveal(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
@ -35,6 +31,6 @@ impl Tab {
self.cd(parent.clone());
FilesOp::Creating(parent, vec![File::from_dummy(&opt.target)]).emit();
Manager::_hover(Some(opt.target));
ManagerProxy::hover(Some(opt.target));
}
}

View file

@ -1,13 +1,14 @@
use std::{mem, time::Duration};
use std::{fmt::Display, mem, time::Duration};
use anyhow::bail;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_plugin::external;
use yazi_proxy::{InputProxy, ManagerProxy, TabProxy};
use yazi_shared::{event::Cmd, fs::FilesOp, render};
use crate::{input::Input, manager::Manager, tab::Tab};
use crate::tab::Tab;
#[derive(PartialEq, Eq)]
pub enum OptType {
@ -26,14 +27,13 @@ impl From<String> for OptType {
}
}
impl ToString for OptType {
fn to_string(&self) -> String {
match self {
impl Display for OptType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Rg => "rg",
Self::Fd => "fd",
Self::None => "none",
}
.to_owned()
})
}
}
@ -60,7 +60,7 @@ impl Tab {
let hidden = self.conf.show_hidden;
self.search = Some(tokio::spawn(async move {
let mut input = Input::_show(InputCfg::search(&opt.type_.to_string()));
let mut input = InputProxy::show(InputCfg::search(&opt.type_.to_string()));
let Some(Ok(subject)) = input.recv().await else { bail!("") };
cwd = cwd.into_search(subject.clone());
@ -73,7 +73,7 @@ impl Tab {
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300));
pin!(rx);
let ((), ticket) = (Tab::_cd(&cwd), FilesOp::prepare(&cwd));
let ((), ticket) = (TabProxy::cd(&cwd), FilesOp::prepare(&cwd));
while let Some(chunk) = rx.next().await {
FilesOp::Part(cwd.clone(), chunk, ticket).emit();
}
@ -91,7 +91,7 @@ impl Tab {
if self.current.cwd.is_search() {
let rep = self.history_new(&self.current.cwd.to_regular());
drop(mem::replace(&mut self.current, rep));
Manager::_refresh();
ManagerProxy::refresh();
}
}
}

View file

@ -1,6 +1,7 @@
use std::borrow::Cow;
use yazi_shared::{event::Cmd, fs::Url, render};
use yazi_proxy::AppProxy;
use yazi_shared::{event::Cmd, fs::Url, render, render_and};
use crate::tab::Tab;
@ -30,10 +31,17 @@ impl<'a> Tab {
return;
};
render!(match opt.state {
Some(true) => self.selected.add(&url),
Some(false) => self.selected.remove(&url),
None => self.selected.remove(&url) || self.selected.add(&url),
});
let b = match opt.state {
Some(true) => render_and!(self.selected.add(&url)),
Some(false) => render_and!(self.selected.remove(&url)) | true,
None => render_and!(self.selected.remove(&url) || self.selected.add(&url)),
};
if !b {
AppProxy::notify_warn(
"Select one",
"This file cannot be selected, due to path nesting conflict.",
);
}
}
}

View file

@ -1,3 +1,4 @@
use yazi_proxy::AppProxy;
use yazi_shared::{event::Cmd, render};
use crate::tab::Tab;
@ -23,24 +24,24 @@ impl From<Option<bool>> for Opt {
impl Tab {
pub fn select_all(&mut self, opt: impl Into<Opt>) {
let mut b = false;
match opt.into().state {
Some(true) => {
for f in self.current.files.iter() {
b |= self.selected.add(&f.url);
}
}
Some(false) => {
for f in self.current.files.iter() {
b |= self.selected.remove(&f.url);
}
}
None => {
for f in self.current.files.iter() {
b |= self.selected.remove(&f.url) || self.selected.add(&f.url);
}
}
let iter = self.current.files.iter().map(|f| &f.url);
let (removal, addition): (Vec<_>, Vec<_>) = match opt.into().state {
Some(true) => (vec![], iter.collect()),
Some(false) => (iter.collect(), vec![]),
None => iter.partition(|&u| self.selected.contains(u)),
};
let same = !self.current.cwd.is_search();
render!(self.selected.remove_many(&removal, same) > 0);
let added = self.selected.add_many(&addition, same);
render!(added > 0);
if added != addition.len() {
AppProxy::notify_warn(
"Select all",
"Some files cannot be selected, due to path nesting conflict.",
);
}
render!(b);
}
}

View file

@ -1,10 +1,11 @@
use yazi_config::{open::Opener, popup::InputCfg};
use yazi_proxy::{InputProxy, TasksProxy};
use yazi_shared::event::Cmd;
use crate::{input::Input, tab::Tab, tasks::Tasks};
use crate::tab::Tab;
pub struct Opt {
exec: String,
run: String,
block: bool,
confirm: bool,
}
@ -12,7 +13,7 @@ pub struct Opt {
impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self {
Self {
exec: c.take_first().unwrap_or_default(),
run: c.take_first().unwrap_or_default(),
block: c.named.contains_key("block"),
confirm: c.named.contains_key("confirm"),
}
@ -26,19 +27,19 @@ impl Tab {
}
let mut opt = opt.into() as Opt;
let selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect();
let selected = self.hovered_and_selected().into_iter().cloned().collect();
tokio::spawn(async move {
if !opt.confirm || opt.exec.is_empty() {
let mut result = Input::_show(InputCfg::shell(opt.block).with_value(opt.exec));
if !opt.confirm || opt.run.is_empty() {
let mut result = InputProxy::show(InputCfg::shell(opt.block).with_value(opt.run));
match result.recv().await {
Some(Ok(e)) => opt.exec = e,
Some(Ok(e)) => opt.run = e,
_ => return,
}
}
Tasks::_open(selected, Opener {
exec: opt.exec,
TasksProxy::open_with(selected, Opener {
run: opt.run,
block: opt.block,
orphan: false,
desc: Default::default(),

View file

@ -1,9 +1,10 @@
use std::str::FromStr;
use yazi_config::manager::SortBy;
use yazi_proxy::ManagerProxy;
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tab::Tab, tasks::Tasks};
use crate::{tab::Tab, tasks::Tasks};
impl Tab {
pub fn sort(&mut self, c: Cmd, tasks: &Tasks) {
@ -15,7 +16,7 @@ impl Tab {
self.conf.sort_dir_first = c.named.contains_key("dir-first");
self.apply_files_attrs();
Manager::_update_paged();
ManagerProxy::update_paged();
tasks.preload_sorted(&self.current.files);
}

View file

@ -1,4 +1,4 @@
use std::{collections::BTreeSet, mem};
use std::{collections::BTreeSet, fmt::Display, mem};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum Mode {
@ -37,13 +37,12 @@ impl Mode {
pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) }
}
impl ToString for Mode {
fn to_string(&self) -> String {
match self {
impl Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Mode::Normal => "normal",
Mode::Select(..) => "select",
Mode::Unset(..) => "unset",
}
.to_string()
})
}
}

View file

@ -15,90 +15,86 @@ impl Deref for Selected {
}
impl Selected {
pub fn add(&mut self, url: &Url) -> bool { self.add_many(&[url]) }
#[inline]
pub fn add(&mut self, url: &Url) -> bool { self.add_same(&[url]) == 1 }
/// Adds a list of URLs to the user structure.
///
/// This method attempts to add a slice of `Url` references to the internal
/// structure, ensuring that all URLs have the same parent directory. For
/// example, URLs such as `/a/b/c`, `/a/b/d`, `/a/b/e`, and `/a/b/f` are
/// acceptable, while `/a/b/c` and `/a/e/f` would not be, due to differing
/// parent directories.
///
/// The addition will fail under the following conditions:
/// - Any of the URLs already exists within the `inner` collection.
/// - The parent directory of the URLs already exists as a key in the
/// `parents` map.
///
/// When the provided list of URLs is empty, the method will return `true` as
/// there are no URLs to process, which is considered a successful operation.
///
/// # Arguments
///
/// * `urls` - A slice of references to `Url` objects that are to be added.
/// All URLs should have the same parent path.
///
/// # Returns
///
/// Returns `true` if all URLs were successfully added, or if the input list
/// is empty. Returns `false` if any URL could not be added due to the
/// existence of its parent directory in the structure, or if the URL itself
/// is already present.
///
/// # Examples
///
/// ```
/// # use yazi_core::tab::Selected;
/// # use yazi_shared::fs::Url;
/// let mut s = Selected::default();
///
/// let url1 = Url::from("/a/b/c");
/// let url2 = Url::from("/a/b/d");
/// assert!(s.add_many(&[&url1, &url2]));
/// ```
pub fn add_many(&mut self, urls: &[&Url]) -> bool {
if urls.is_empty() {
return true;
} else if self.parents.contains_key(urls[0]) {
return false;
pub fn add_many(&mut self, urls: &[&Url], same: bool) -> usize {
if same {
return self.add_same(urls);
}
let mut grouped: HashMap<_, Vec<_>> = Default::default();
for &u in urls {
if let Some(p) = u.parent_url() {
grouped.entry(p).or_default().push(u);
}
}
grouped.into_values().map(|v| self.add_same(&v)).sum()
}
fn add_same(&mut self, urls: &[&Url]) -> usize {
// If it has appeared as a parent
let urls: Vec<_> = urls.iter().filter(|&&u| !self.parents.contains_key(u)).collect();
if urls.is_empty() {
return 0;
}
// If it has appeared as a child
let mut parent = urls[0].parent_url();
let mut parents = vec![];
while let Some(u) = parent {
if self.inner.contains(&u) {
return false;
return 0;
}
parent = u.parent_url();
parents.push(u);
}
for u in parents {
*self.parents.entry(u).or_insert(0) += urls.len();
}
let len = self.inner.len();
self.inner.extend(urls.iter().map(|&&u| u.clone()));
self.inner.extend(urls.iter().map(|&u| u.clone()));
true
for u in parents {
*self.parents.entry(u).or_insert(0) += self.inner.len() - len;
}
urls.len()
}
pub fn remove(&mut self, url: &Url) -> bool {
if !self.inner.remove(url) {
return false;
#[inline]
pub fn remove(&mut self, url: &Url) -> bool { self.remove_same(&[url]) == 1 }
pub fn remove_many(&mut self, urls: &[&Url], same: bool) -> usize {
if same {
return self.remove_same(urls);
}
let mut parent = url.parent_url();
let mut grouped: HashMap<_, Vec<_>> = Default::default();
for &u in urls {
if let Some(p) = u.parent_url() {
grouped.entry(p).or_default().push(u);
}
}
grouped.into_values().map(|v| self.remove_same(&v)).sum()
}
fn remove_same(&mut self, urls: &[&Url]) -> usize {
let count = urls.iter().map(|&u| self.inner.remove(u)).filter(|&b| b).count();
if count == 0 {
return 0;
}
let mut parent = urls[0].parent_url();
while let Some(u) = parent {
let n = self.parents.get_mut(&u).unwrap();
if *n == 1 {
*n -= count;
if *n == 0 {
self.parents.remove(&u);
} else {
*n -= 1;
}
parent = u.parent_url();
}
true
count
}
pub fn clear(&mut self) {
@ -153,11 +149,14 @@ mod tests {
fn insert_many_success() {
let mut s = Selected::default();
assert!(s.add_many(&[
&Url::from("/parent/child1"),
&Url::from("/parent/child2"),
&Url::from("/parent/child3")
]));
assert_eq!(
3,
s.add_same(&[
&Url::from("/parent/child1"),
&Url::from("/parent/child2"),
&Url::from("/parent/child3")
])
);
}
#[test]
@ -165,7 +164,7 @@ mod tests {
let mut s = Selected::default();
s.add(&Url::from("/parent"));
assert!(!s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2"),]));
assert_eq!(0, s.add_same(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")]));
}
#[test]
@ -173,14 +172,14 @@ mod tests {
let mut s = Selected::default();
s.add(&Url::from("/parent/child1"));
assert!(s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")]));
assert_eq!(2, s.add_same(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")]));
}
#[test]
fn insert_many_empty_urls_list() {
let mut s = Selected::default();
assert!(s.add_many(&[]));
assert_eq!(0, s.add_same(&[]));
}
#[test]
@ -188,14 +187,17 @@ mod tests {
let mut s = Selected::default();
s.add(&Url::from("/parent/child"));
assert!(!s.add_many(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")]));
assert_eq!(
0,
s.add_same(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")])
);
}
#[test]
fn insert_many_with_direct_parent_fails() {
let mut s = Selected::default();
s.add(&Url::from("/a"));
assert!(!s.add_many(&[&Url::from("/a/b")]));
assert_eq!(0, s.add_same(&[&Url::from("/a/b")]));
}
#[test]
@ -203,14 +205,15 @@ mod tests {
let mut s = Selected::default();
s.add(&Url::from("/a/b"));
assert!(!s.add_many(&[&Url::from("/a")]));
assert_eq!(0, s.add_same(&[&Url::from("/a")]));
assert_eq!(1, s.add_same(&[&Url::from("/b"), &Url::from("/a")]));
}
#[test]
fn insert_many_sibling_directories_success() {
let mut s = Selected::default();
assert!(s.add_many(&[&Url::from("/a/b"), &Url::from("/a/c")]));
assert_eq!(2, s.add_same(&[&Url::from("/a/b"), &Url::from("/a/c")]));
}
#[test]
@ -218,7 +221,7 @@ mod tests {
let mut s = Selected::default();
s.add(&Url::from("/a/b"));
assert!(!s.add_many(&[&Url::from("/a/b/c")]));
assert_eq!(0, s.add_same(&[&Url::from("/a/b/c")]));
}
#[test]
@ -228,7 +231,7 @@ mod tests {
let child1 = Url::from("/parent/child1");
let child2 = Url::from("/parent/child2");
let child3 = Url::from("/parent/child3");
assert!(s.add_many(&[&child1, &child2, &child3]));
assert_eq!(3, s.add_same(&[&child1, &child2, &child3]));
assert!(s.remove(&child1));
assert_eq!(s.inner.len(), 2);

View file

@ -58,6 +58,18 @@ impl Tab {
}
}
pub fn hovered_and_selected(&self) -> Vec<&Url> {
let Some(h) = self.current.hovered() else {
return vec![];
};
if self.selected.is_empty() {
vec![&h.url, &h.url]
} else {
[&h.url].into_iter().chain(self.selected.iter()).collect()
}
}
// --- History
#[inline]
pub fn history_new(&mut self, url: &Url) -> Folder {

View file

@ -2,7 +2,8 @@ use std::io::{stdout, Write};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::AppProxy;
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, term::Term, Defer};
use crate::tasks::Tasks;
@ -26,10 +27,10 @@ impl Tasks {
task.logs.clone()
};
Scheduler::app_stop().await;
AppProxy::stop().await;
let _defer = Defer::new(|| {
disable_raw_mode().ok();
Scheduler::app_resume();
AppProxy::resume();
});
Term::clear(&mut stdout()).ok();

View file

@ -1,5 +1,5 @@
mod arrow;
mod cancel;
mod inspect;
mod open;
mod open_with;
mod toggle;

View file

@ -1,27 +0,0 @@
use yazi_config::open::Opener;
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use crate::tasks::Tasks;
pub struct Opt {
targets: Vec<Url>,
opener: Opener,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Tasks {
pub fn _open(targets: Vec<Url>, opener: Opener) {
emit!(Call(Cmd::new("open").with_data(Opt { targets, opener }), Layer::Tasks));
}
pub fn open(&mut self, opt: impl TryInto<Opt>) {
if let Ok(opt) = opt.try_into() {
self.file_open_with(&opt.opener, &opt.targets);
}
}
}

View file

@ -0,0 +1,11 @@
use yazi_proxy::options::OpenWithOpt;
use crate::tasks::Tasks;
impl Tasks {
pub fn open_with(&mut self, opt: impl TryInto<OpenWithOpt>) {
if let Ok(opt) = opt.try_into() {
self.file_open_with(&opt.opener, &opt.targets);
}
}
}

View file

@ -1,4 +1,4 @@
use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path, sync::Arc, time::Duration};
use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, sync::Arc, time::Duration};
use tokio::time::sleep;
use tracing::debug;
@ -56,28 +56,26 @@ impl Tasks {
running.values().take(Self::limit()).map(Into::into).collect()
}
pub fn file_open(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> bool {
pub fn file_open(&self, hovered: &Url, targets: &[(Url, String)]) {
let mut openers = BTreeMap::new();
for (path, mime) in targets {
if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first().copied()) {
openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str());
for (url, mime) in targets {
if let Some(opener) = OPEN.openers(url, mime).and_then(|o| o.first().copied()) {
openers.entry(opener).or_insert_with(|| vec![hovered]).push(url);
}
}
for (opener, args) in openers {
self.file_open_with(opener, &args);
}
false
}
pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef<OsStr>]) -> bool {
pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef<OsStr>]) {
if opener.spread {
self.scheduler.process_open(opener, args);
return false;
return;
}
for target in args {
self.scheduler.process_open(opener, &[target]);
for target in args.iter().skip(1) {
self.scheduler.process_open(opener, &[&args[0], target]);
}
false
}
pub fn file_cut(&self, src: &HashSet<Url>, dest: &Url, force: bool) {

View file

@ -33,7 +33,7 @@ impl WhichSorter {
entities.push(match self.by {
SortBy::None => unreachable!(),
SortBy::Key => Cow::Owned(ctrl.on()),
SortBy::Desc => ctrl.desc_or_exec(),
SortBy::Desc => ctrl.desc_or_run(),
});
}

View file

@ -14,6 +14,7 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-core = { path = "../yazi-core", version = "0.2.3" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }

View file

@ -15,7 +15,7 @@ pub(crate) struct App {
}
impl App {
pub(crate) async fn run() -> Result<()> {
pub(crate) async fn serve() -> Result<()> {
let term = Term::start()?;
let signals = Signals::start()?;

View file

@ -1,7 +1,13 @@
use yazi_core::notify::Message;
use yazi_proxy::options::NotifyOpt;
use crate::app::App;
impl App {
pub(crate) fn notify(&mut self, msg: impl TryInto<Message>) { self.cx.notify.push(msg); }
pub(crate) fn notify(&mut self, opt: impl TryInto<NotifyOpt>) {
let Ok(opt) = opt.try_into() else {
return;
};
self.cx.notify.push(opt);
}
}

View file

@ -151,11 +151,11 @@ impl<'a> Executor<'a> {
};
}
on!(open);
on!(toggle, "close");
on!(arrow);
on!(inspect);
on!(cancel);
on!(open_with);
#[allow(clippy::single_match)]
match cmd.name.as_str() {

View file

@ -22,9 +22,9 @@ impl Widget for Bindings<'_> {
let col1: Vec<_> =
bindings.iter().map(|c| ListItem::new(c.on()).style(THEME.help.on)).collect();
// Exec
// Run
let col2: Vec<_> =
bindings.iter().map(|c| ListItem::new(c.exec()).style(THEME.help.exec)).collect();
bindings.iter().map(|c| ListItem::new(c.run()).style(THEME.help.run)).collect();
// Desc
let col3: Vec<_> = bindings

View file

@ -19,7 +19,7 @@ impl<'a> Widget for Layout<'a> {
let chunks = layout::Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).split(area);
Line::styled(
help.keyword().unwrap_or_else(|| format!("{}.help", help.layer.to_string())),
help.keyword().unwrap_or_else(|| format!("{}.help", help.layer)),
THEME.help.footer,
)
.render(chunks[1], buf);

View file

@ -4,7 +4,7 @@ use mlua::{Scope, Table};
use tracing::error;
use yazi_config::LAYOUT;
use yazi_plugin::{elements::RectRef, LUA};
use yazi_shared::RoCell;
use yazi_shared::{Defer, RoCell};
use crate::Ctx;
@ -34,6 +34,7 @@ impl Lives {
f: impl FnOnce(&Scope<'a, 'a>) -> mlua::Result<T>,
) -> mlua::Result<T> {
let result = LUA.scope(|scope| {
let _defer = Defer::new(|| SCOPE.drop());
SCOPE.init(unsafe { mem::transmute(scope) });
LUA.set_named_registry_value("cx", scope.create_any_userdata_ref(cx)?)?;
@ -58,7 +59,6 @@ impl Lives {
status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?,
}));
SCOPE.drop();
Ok(ret)
});

View file

@ -8,7 +8,10 @@ pub(super) struct Logs;
impl Logs {
pub(super) fn start() {
let appender = tracing_appender::rolling::never(Xdg::state_dir().unwrap(), "yazi.log");
let state_dir = Xdg::state_dir();
std::fs::create_dir_all(&state_dir).expect("Failed to create state directory");
let appender = tracing_appender::rolling::never(state_dir, "yazi.log");
let (handle, guard) = tracing_appender::non_blocking(appender);
// let filter = EnvFilter::from_default_env();

View file

@ -52,5 +52,5 @@ async fn main() -> anyhow::Result<()> {
yazi_core::init();
app::App::run().await
app::App::serve().await
}

View file

@ -1,7 +1,9 @@
use std::rc::Rc;
use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, style::{Style, Stylize}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}};
use yazi_core::notify::{Level, Message};
use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}};
use yazi_config::THEME;
use yazi_core::notify::Message;
use yazi_proxy::options::NotifyLevel;
use crate::{widgets::Clear, Ctx};
@ -42,9 +44,9 @@ impl<'a> Widget for Layout<'a> {
for (i, m) in notify.messages.iter().enumerate().take(limit) {
let (icon, style) = match m.level {
Level::Info => ("", Style::default().green()),
Level::Warn => ("", Style::default().yellow()),
Level::Error => ("", Style::default().red()),
NotifyLevel::Info => (&THEME.notify.icon_info, THEME.notify.title_info),
NotifyLevel::Warn => (&THEME.notify.icon_warn, THEME.notify.title_warn),
NotifyLevel::Error => (&THEME.notify.icon_error, THEME.notify.title_error),
};
let mut rect =

View file

@ -43,7 +43,8 @@ impl Signals {
#[cfg(unix)]
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::AppProxy;
use yazi_scheduler::BLOCKER;
let mut signals = signal_hook_tokio::Signals::new([
// Terminating signals
@ -65,7 +66,7 @@ impl Signals {
break;
}
}
SIGCONT => Scheduler::app_resume(),
SIGCONT => AppProxy::resume(),
_ => {}
}
}

View file

@ -32,7 +32,7 @@ impl Widget for Cand<'_> {
spans.push(Span::styled(&THEME.which.separator, THEME.which.separator_style));
// Description
spans.push(Span::styled(self.cand.desc_or_exec(), THEME.which.desc));
spans.push(Span::styled(self.cand.desc_or_run(), THEME.which.desc));
Line::from(spans).render(area, buf);
}

View file

@ -12,6 +12,7 @@ repository = "https://github.com/sxyazi/yazi"
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" }
yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }
# External dependencies

View file

@ -3,7 +3,7 @@ local M = {}
function M:peek()
local _, bound = ya.preview_archive(self)
if bound then
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
end
end
@ -12,7 +12,7 @@ function M:seek(units)
if h and h.url == self.file.url then
local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", {
tostring(math.max(0, cx.active.preview.skip + step)),
math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url),
})
end

View file

@ -3,7 +3,7 @@ local M = {}
function M:peek()
local _, bound = ya.preview_code(self)
if bound then
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
end
end
@ -12,7 +12,7 @@ function M:seek(units)
if h and h.url == self.file.url then
local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", {
tostring(math.max(0, cx.active.preview.skip + step)),
math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url),
})
end

View file

@ -8,7 +8,7 @@ function M:peek()
local bound = math.max(0, #folder.files - self.area.h)
if self.skip > bound then
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
end
local items, markers = {}, {}
@ -37,7 +37,7 @@ function M:seek(units)
local step = math.floor(units * self.area.h / 10)
local bound = math.max(0, #folder.files - self.area.h)
ya.manager_emit("peek", {
tostring(ya.clamp(0, cx.active.preview.skip + step, bound)),
ya.clamp(0, cx.active.preview.skip + step, bound),
only_if = tostring(self.file.url),
})
end

View file

@ -34,7 +34,7 @@ function M:peek()
child:start_kill()
if self.skip > 0 and i < self.skip + limit then
ya.manager_emit("peek", { tostring(math.max(0, i - limit)), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { math.max(0, i - limit), only_if = tostring(self.file.url), upper_bound = true })
else
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) })
@ -46,7 +46,7 @@ function M:seek(units)
if h and h.url == self.file.url then
local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", {
tostring(math.max(0, cx.active.preview.skip + step)),
math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url),
})
end
@ -55,7 +55,7 @@ end
function M:fallback_to_builtin()
local _, bound = ya.preview_code(self)
if bound then
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
end
end

View file

@ -15,9 +15,10 @@ function M:preload()
urls[#urls + 1] = tostring(file.url)
end
local child, code = Command("file"):args({ "-bL", "--mime-type" }):args(urls):stdout(Command.PIPED):spawn()
local cmd = os.getenv("YAZI_FILE_ONE") or "file"
local child, code = Command(cmd):args({ "-bL", "--mime-type" }):args(urls):stdout(Command.PIPED):spawn()
if not child then
ya.err("spawn `file` command returns " .. tostring(code))
ya.err(string.format("spawn `%s` command returns %s", cmd, code))
return 0
end

View file

@ -16,7 +16,7 @@ function M:seek(units)
local h = cx.active.current.hovered
if h and h.url == self.file.url then
local step = ya.clamp(-1, units, 1)
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)), only_if = tostring(self.file.url) })
ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url) })
end
end
@ -37,7 +37,7 @@ function M:preload()
elseif not output.status:success() then
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
if self.skip > 0 and pages > 0 then
ya.manager_emit("peek", { tostring(math.max(0, pages - 1)), only_if = tostring(self.file.url), upper_bound = "" })
ya.manager_emit("peek", { math.max(0, pages - 1), only_if = tostring(self.file.url), upper_bound = true })
end
return 0
end

Some files were not shown because too many files have changed in this diff Show more