mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Reformat
This commit is contained in:
parent
9fc3d9fa10
commit
d7d9ef12b3
8 changed files with 214 additions and 819 deletions
332
Cargo.lock
generated
332
Cargo.lock
generated
|
|
@ -1022,17 +1022,6 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.12"
|
||||
|
|
@ -1297,15 +1286,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
|
|
@ -1472,19 +1452,6 @@ version = "0.32.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.18"
|
||||
|
|
@ -1655,135 +1622,12 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"potential_utf",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.8"
|
||||
|
|
@ -2088,18 +1932,6 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.17.0+1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
|
|
@ -2116,18 +1948,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
|
|
@ -2140,12 +1960,6 @@ version = "0.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
|
|
@ -2879,15 +2693,6 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
|
@ -3729,12 +3534,6 @@ dependencies = [
|
|||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
|
@ -3786,17 +3585,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "5.3.0"
|
||||
|
|
@ -3944,16 +3732,6 @@ dependencies = [
|
|||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
|
|
@ -4242,30 +4020,12 @@ version = "0.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
|
@ -4310,12 +4070,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "9.0.6"
|
||||
|
|
@ -4874,12 +4628,6 @@ version = "0.46.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
|
@ -5164,9 +4912,7 @@ dependencies = [
|
|||
"core-foundation-sys",
|
||||
"dirs",
|
||||
"foldhash 0.2.0",
|
||||
"git2",
|
||||
"hashbrown 0.16.0",
|
||||
"ignore",
|
||||
"libc",
|
||||
"objc2",
|
||||
"parking_lot",
|
||||
|
|
@ -5414,30 +5160,6 @@ dependencies = [
|
|||
"yazi-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
|
|
@ -5458,66 +5180,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["yazi-*"]
|
||||
default-members = ["yazi-fm", "yazi-cli"]
|
||||
members = [ "yazi-*" ]
|
||||
default-members = [ "yazi-fm", "yazi-cli" ]
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ impl Actor for Ignore {
|
|||
const NAME: &str = "ignore";
|
||||
|
||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||
let gitignores = YAZI.files.gitignores;
|
||||
|
||||
// Get the appropriate context string for matching exclude rules
|
||||
// For search directories, use "search://**" as the context
|
||||
let cwd = cx.cwd();
|
||||
|
|
@ -32,20 +30,17 @@ impl Actor for Ignore {
|
|||
let exclude_patterns = YAZI.files.excludes_for_context(&cwd_str);
|
||||
|
||||
// Create glob matcher function for compiled patterns
|
||||
let glob_matcher = {
|
||||
let glob_matcher: Option<Arc<dyn Fn(&std::path::Path) -> Option<bool> + Send + Sync>> =
|
||||
if !exclude_patterns.is_empty() {
|
||||
let context = cwd_str.clone();
|
||||
Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context))
|
||||
};
|
||||
|
||||
// If gitignores is disabled but we have exclude patterns, apply them
|
||||
if !gitignores && !exclude_patterns.is_empty() {
|
||||
// Load ignore filter from exclude patterns only
|
||||
let ignore_filter = if let Some(path) = cx.cwd().as_path() {
|
||||
IgnoreFilter::from_patterns(path, &exclude_patterns, Some(glob_matcher.clone()))
|
||||
Some(Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Load ignore filter from exclude patterns
|
||||
let ignore_filter = IgnoreFilter::from_patterns(glob_matcher.clone());
|
||||
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
let apply = |f: &mut Folder, filter: Option<IgnoreFilter>| {
|
||||
// Always set the filter, even when loading
|
||||
|
|
@ -72,112 +67,14 @@ impl Actor for Ignore {
|
|||
h.url.as_path().map(|p| p.display().to_string()).unwrap_or_default()
|
||||
};
|
||||
let hovered_excludes = YAZI.files.excludes_for_context(&hovered_str);
|
||||
let hovered_matcher = {
|
||||
let hovered_matcher: Option<Arc<dyn Fn(&std::path::Path) -> Option<bool> + Send + Sync>> =
|
||||
if !hovered_excludes.is_empty() {
|
||||
let context = hovered_str.clone();
|
||||
Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context))
|
||||
};
|
||||
let hovered_filter = if let Some(path) = h.url.as_path() {
|
||||
IgnoreFilter::from_patterns(path, &hovered_excludes, Some(hovered_matcher))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if apply(h, hovered_filter) {
|
||||
render!(h.repos(None));
|
||||
act!(mgr:peek, cx, true)?;
|
||||
} else if hovered.as_deref() != cx.hovered().map(|f| f.urn()) {
|
||||
act!(mgr:peek, cx)?;
|
||||
act!(mgr:watch, cx)?;
|
||||
}
|
||||
}
|
||||
|
||||
succ!();
|
||||
}
|
||||
|
||||
// If gitignores is disabled and no exclude patterns, remove any ignore filter
|
||||
if !gitignores {
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
let apply = |f: &mut Folder| {
|
||||
// Always clear the filter, even when loading
|
||||
let changed = f.files.set_ignore_filter(None);
|
||||
if f.stage == FolderStage::Loading {
|
||||
render!();
|
||||
false
|
||||
} else {
|
||||
render_and!(changed && f.files.catchup_revision())
|
||||
}
|
||||
};
|
||||
|
||||
// Apply to CWD and parent
|
||||
if let (a, Some(b)) = (apply(cx.current_mut()), cx.parent_mut().map(apply))
|
||||
&& (a | b)
|
||||
{
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:update_paged, cx)?;
|
||||
}
|
||||
|
||||
// Apply to hovered
|
||||
if let Some(h) = cx.hovered_folder_mut()
|
||||
&& apply(h)
|
||||
{
|
||||
render!(h.repos(None));
|
||||
act!(mgr:peek, cx, true)?;
|
||||
} else if hovered.as_deref() != cx.hovered().map(|f| f.urn()) {
|
||||
act!(mgr:peek, cx)?;
|
||||
act!(mgr:watch, cx)?;
|
||||
}
|
||||
|
||||
succ!();
|
||||
}
|
||||
|
||||
// Load ignore filter from the current directory
|
||||
let ignore_filter = if let Some(path) = cx.cwd().as_path() {
|
||||
IgnoreFilter::from_dir(path, &exclude_patterns, gitignores, Some(glob_matcher.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
let apply = |f: &mut Folder, filter: Option<IgnoreFilter>| {
|
||||
// Always set the filter, even when loading, so files are filtered as they
|
||||
// arrive
|
||||
let changed = f.files.set_ignore_filter(filter);
|
||||
if f.stage == FolderStage::Loading {
|
||||
render!();
|
||||
false
|
||||
} else {
|
||||
render_and!(changed && f.files.catchup_revision())
|
||||
}
|
||||
};
|
||||
|
||||
// Apply to CWD
|
||||
if apply(cx.current_mut(), ignore_filter.clone()) {
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:update_paged, cx)?;
|
||||
}
|
||||
|
||||
// Apply to parent (they should use their own ignore file, so we don't apply the
|
||||
// same filter) Parent folders will be updated when they become the current
|
||||
// directory
|
||||
|
||||
// Apply to hovered
|
||||
if let Some(h) = cx.hovered_folder_mut() {
|
||||
// Load ignore filter for hovered directory if it's a directory
|
||||
let hovered_str = if h.url.is_search() {
|
||||
"search://**".to_string()
|
||||
} else {
|
||||
h.url.as_path().map(|p| p.display().to_string()).unwrap_or_default()
|
||||
};
|
||||
let hovered_excludes = YAZI.files.excludes_for_context(&hovered_str);
|
||||
let hovered_matcher = {
|
||||
let context = hovered_str.clone();
|
||||
Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context))
|
||||
};
|
||||
let hovered_filter = if let Some(path) = h.url.as_path() {
|
||||
IgnoreFilter::from_dir(path, &hovered_excludes, gitignores, Some(hovered_matcher))
|
||||
Some(Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let hovered_filter = IgnoreFilter::from_patterns(hovered_matcher);
|
||||
|
||||
if apply(h, hovered_filter) {
|
||||
render!(h.repos(None));
|
||||
|
|
|
|||
|
|
@ -16,26 +16,6 @@ scrolloff = 5
|
|||
mouse_events = [ "click", "scroll" ]
|
||||
title_format = "Yazi: {cwd}"
|
||||
|
||||
[files]
|
||||
# Enable git ignore integration
|
||||
# gitignores = false
|
||||
|
||||
# Context-specific exclude patterns
|
||||
# Patterns are compiled into glob matchers for efficient matching
|
||||
# Patterns starting with '!' negate (whitelist) previous matches
|
||||
# excludes = [
|
||||
# # SFTP temporary files
|
||||
# { urn = "*.tmp", in = "sftp://**" },
|
||||
# # Python cache in search results
|
||||
# { urn = "/root/**/*.pyc", in = "search://**" },
|
||||
# # Multiple patterns for /code directory (supports arrays)
|
||||
# { urn = [".git", ".DS_Store", "__pycache__"], in = "/code/**" },
|
||||
# # Negation: show target/ even if previously ignored
|
||||
# { urn = "!target/", in = "/code/**" },
|
||||
# # Fallback rule for all contexts
|
||||
# { urn = ".DS_Store", in = "*" },
|
||||
# ]
|
||||
|
||||
[preview]
|
||||
wrap = "no"
|
||||
tab_size = 2
|
||||
|
|
@ -46,7 +26,28 @@ image_delay = 30
|
|||
image_filter = "triangle"
|
||||
image_quality = 75
|
||||
ueberzug_scale = 1
|
||||
ueberzug_offset = [0, 0, 0, 0]
|
||||
ueberzug_offset = [ 0, 0, 0, 0 ]
|
||||
|
||||
|
||||
[files]
|
||||
# Context-specific exclude patterns
|
||||
# Patterns are compiled into glob matchers for efficient matching
|
||||
# Patterns starting with '!' negate (whitelist) previous matches
|
||||
# Note: To match directories, use patterns like ".git/" or "**/.git/**"
|
||||
# excludes = [
|
||||
# # SFTP temporary files
|
||||
# { urn = "*.tmp", in = "sftp://**" },
|
||||
# # Python cache in search results
|
||||
# { urn = "/root/**/*.pyc", in = "search://**" },
|
||||
# # Multiple patterns for /code directory (supports arrays)
|
||||
# { urn = [".git", ".DS_Store", "__pycache__"], in = "*" },
|
||||
# # Exclude release and debug directories inside target/
|
||||
# { urn = "release", in = "**/target" },
|
||||
# # Exclude all json files from all contexts
|
||||
# { urn = "*.json", in = "*" },
|
||||
# # Negation: Show cspell.* even if it's excluded by a previous rule
|
||||
# { urn = "!cspell*", in = "search://**" },
|
||||
# ]
|
||||
|
||||
[opener]
|
||||
edit = [
|
||||
|
|
@ -75,7 +76,9 @@ reveal = [
|
|||
{ run = "termux-open %d1", desc = "Reveal", for = "android" },
|
||||
{ run = "clear; exiftool %s1; echo 'Press enter to exit'; read _", desc = "Show EXIF", for = "unix", block = true },
|
||||
]
|
||||
extract = [{ run = "ya pub extract --list %s", desc = "Extract here" }]
|
||||
extract = [
|
||||
{ run = "ya pub extract --list %s", desc = "Extract here" },
|
||||
]
|
||||
download = [
|
||||
{ run = "ya emit download --open %S", desc = "Download and open" },
|
||||
{ run = "ya emit download %S", desc = "Download" },
|
||||
|
|
@ -84,52 +87,24 @@ download = [
|
|||
[open]
|
||||
rules = [
|
||||
# Folder
|
||||
{ url = "*/", use = [
|
||||
"edit",
|
||||
"open",
|
||||
"reveal",
|
||||
] },
|
||||
{ url = "*/", use = [ "edit", "open", "reveal" ] },
|
||||
# Text
|
||||
{ mime = "text/*", use = [
|
||||
"edit",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "text/*", use = [ "edit", "reveal" ] },
|
||||
# Image
|
||||
{ mime = "image/*", use = [
|
||||
"open",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "image/*", use = [ "open", "reveal" ] },
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", use = [
|
||||
"play",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "{audio,video}/*", use = [ "play", "reveal" ] },
|
||||
# Archive
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", use = [
|
||||
"extract",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", use = [ "extract", "reveal" ] },
|
||||
# JSON
|
||||
{ mime = "application/{json,ndjson}", use = [
|
||||
"edit",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "*/javascript", use = [
|
||||
"edit",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "application/{json,ndjson}", use = [ "edit", "reveal" ] },
|
||||
{ mime = "*/javascript", use = [ "edit", "reveal" ] },
|
||||
# Empty file
|
||||
{ mime = "inode/empty", use = [
|
||||
"edit",
|
||||
"reveal",
|
||||
] },
|
||||
{ mime = "inode/empty", use = [ "edit", "reveal" ] },
|
||||
# Virtual file system
|
||||
{ mime = "vfs/{absent,stale}", use = "download" },
|
||||
# Fallback
|
||||
{ url = "*", use = [
|
||||
"open",
|
||||
"reveal",
|
||||
] },
|
||||
{ url = "*", use = [ "open", "reveal" ] },
|
||||
]
|
||||
|
||||
[tasks]
|
||||
|
|
@ -137,7 +112,7 @@ micro_workers = 10
|
|||
macro_workers = 10
|
||||
bizarre_retry = 3
|
||||
image_alloc = 536870912 # 512MB
|
||||
image_bound = [10000, 10000]
|
||||
image_bound = [ 10000, 10000 ]
|
||||
suppress_preload = false
|
||||
|
||||
[plugin]
|
||||
|
|
@ -216,65 +191,65 @@ cursor_blink = false
|
|||
# cd
|
||||
cd_title = "Change directory:"
|
||||
cd_origin = "top-center"
|
||||
cd_offset = [0, 2, 50, 3]
|
||||
cd_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# create
|
||||
create_title = ["Create:", "Create (dir):"]
|
||||
create_title = [ "Create:", "Create (dir):" ]
|
||||
create_origin = "top-center"
|
||||
create_offset = [0, 2, 50, 3]
|
||||
create_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# rename
|
||||
rename_title = "Rename:"
|
||||
rename_origin = "hovered"
|
||||
rename_offset = [0, 1, 50, 3]
|
||||
rename_offset = [ 0, 1, 50, 3 ]
|
||||
|
||||
# filter
|
||||
filter_title = "Filter:"
|
||||
filter_origin = "top-center"
|
||||
filter_offset = [0, 2, 50, 3]
|
||||
filter_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# find
|
||||
find_title = ["Find next:", "Find previous:"]
|
||||
find_title = [ "Find next:", "Find previous:" ]
|
||||
find_origin = "top-center"
|
||||
find_offset = [0, 2, 50, 3]
|
||||
find_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# search
|
||||
search_title = "Search via {n}:"
|
||||
search_origin = "top-center"
|
||||
search_offset = [0, 2, 50, 3]
|
||||
search_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# shell
|
||||
shell_title = ["Shell:", "Shell (block):"]
|
||||
shell_title = [ "Shell:", "Shell (block):" ]
|
||||
shell_origin = "top-center"
|
||||
shell_offset = [0, 2, 50, 3]
|
||||
shell_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
[confirm]
|
||||
# trash
|
||||
trash_title = "Trash {n} selected file{s}?"
|
||||
trash_origin = "center"
|
||||
trash_offset = [0, 0, 70, 20]
|
||||
trash_offset = [ 0, 0, 70, 20 ]
|
||||
|
||||
# delete
|
||||
delete_title = "Permanently delete {n} selected file{s}?"
|
||||
delete_origin = "center"
|
||||
delete_offset = [0, 0, 70, 20]
|
||||
delete_offset = [ 0, 0, 70, 20 ]
|
||||
|
||||
# overwrite
|
||||
overwrite_title = "Overwrite file?"
|
||||
overwrite_body = "Will overwrite the following file:"
|
||||
overwrite_origin = "center"
|
||||
overwrite_offset = [0, 0, 50, 15]
|
||||
overwrite_offset = [ 0, 0, 50, 15 ]
|
||||
|
||||
# quit
|
||||
quit_title = "Quit?"
|
||||
quit_body = "The following tasks are still running, are you sure you want to quit?"
|
||||
quit_origin = "center"
|
||||
quit_offset = [0, 0, 50, 15]
|
||||
quit_offset = [ 0, 0, 50, 15 ]
|
||||
|
||||
[pick]
|
||||
open_title = "Open with:"
|
||||
open_origin = "hovered"
|
||||
open_offset = [0, 1, 50, 7]
|
||||
open_offset = [ 0, 1, 50, 7 ]
|
||||
|
||||
[which]
|
||||
sort_by = "none"
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ pub struct Exclude {
|
|||
|
||||
/// Context where this exclude rule applies
|
||||
/// Supports glob patterns like "/code/**", "sftp://**", "search://**", or "*" for all
|
||||
#[serde(rename = "in")]
|
||||
pub context: String,
|
||||
pub r#in: String,
|
||||
|
||||
#[serde(skip)]
|
||||
compiled: Option<CompiledPatterns>,
|
||||
|
|
@ -78,15 +77,40 @@ impl Exclude {
|
|||
pub fn matches_path(&self, path: &Path) -> Option<bool> {
|
||||
let compiled = self.compiled.as_ref()?;
|
||||
|
||||
// For absolute paths, try both the full path and relative components
|
||||
// This helps patterns like **/.git/** match /home/user/project/.git
|
||||
let paths_to_check: Vec<&Path> = if path.is_absolute() {
|
||||
// Also check each component as if it's relative
|
||||
// This allows **/.git/** to match /home/user/.git/config
|
||||
let mut paths = vec![path];
|
||||
|
||||
// Check if any path component matches by checking relative sub-paths
|
||||
// For /home/user/.git/config, we want to match against .git/config too
|
||||
if let Some(components) = path.to_str() {
|
||||
for (i, _) in components.match_indices('/').skip(1) {
|
||||
if let Some(subpath) = components.get(i + 1..) {
|
||||
paths.push(Path::new(subpath));
|
||||
}
|
||||
}
|
||||
}
|
||||
paths
|
||||
} else {
|
||||
vec![path]
|
||||
};
|
||||
|
||||
// Check whitelist first (negation takes precedence)
|
||||
if compiled.whitelists.is_match(path) {
|
||||
for p in &paths_to_check {
|
||||
if compiled.whitelists.is_match(p) {
|
||||
return Some(false); // Explicitly NOT ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Check ignore patterns
|
||||
if compiled.ignores.is_match(path) {
|
||||
for p in &paths_to_check {
|
||||
if compiled.ignores.is_match(p) {
|
||||
return Some(true); // Should be ignored
|
||||
}
|
||||
}
|
||||
|
||||
None // No match
|
||||
}
|
||||
|
|
@ -94,23 +118,49 @@ impl Exclude {
|
|||
/// Check if this exclude rule applies to the given path context
|
||||
pub fn matches_context(&self, path: &str) -> bool {
|
||||
// Wildcard matches everything
|
||||
if self.context == "*" {
|
||||
if self.r#in == "*" {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle patterns starting with **/ (e.g., **/target or **/target/**)
|
||||
if self.r#in.starts_with("**/") {
|
||||
let pattern = &self.r#in[3..]; // Remove leading "**/", e.g., "target" or "target/**"
|
||||
|
||||
// Strip trailing /** if present
|
||||
let pattern = if let Some(p) = pattern.strip_suffix("/**") {
|
||||
p
|
||||
} else {
|
||||
pattern
|
||||
};
|
||||
|
||||
// Check if path ends with the pattern (e.g., /home/user/project/target matches **/target)
|
||||
if path.ends_with(&format!("/{}", pattern)) || path.ends_with(pattern) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if path contains the pattern as a directory segment
|
||||
if path.contains(&format!("/{}/", pattern)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle glob patterns with wildcard
|
||||
if self.context.ends_with("/**") {
|
||||
let prefix = &self.context[..self.context.len() - 3];
|
||||
if self.r#in.ends_with("/**") {
|
||||
let prefix = &self.r#in[..self.r#in.len() - 3];
|
||||
|
||||
// Check if path starts with prefix (absolute path match)
|
||||
if path.starts_with(prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if path contains the pattern anywhere (for relative patterns like "/target/**")
|
||||
// This allows "/target/**" to match "/home/user/project/target/debug"
|
||||
// Check if path contains the pattern anywhere (for relative patterns like
|
||||
// "/target/**") This allows "/target/**" to match
|
||||
// "/home/user/project/target/debug"
|
||||
if prefix.starts_with('/') && !prefix.starts_with("//") {
|
||||
// Single leading slash means relative pattern - check if path contains this segment
|
||||
// Single leading slash means relative pattern - check if path contains this
|
||||
// segment
|
||||
let pattern = &prefix[1..]; // Remove leading slash
|
||||
if path.contains(&format!("/{}/", pattern)) || path.ends_with(&format!("/{}", pattern)) {
|
||||
return true;
|
||||
|
|
@ -121,6 +171,6 @@ impl Exclude {
|
|||
}
|
||||
|
||||
// Exact match or prefix match for non-wildcard patterns
|
||||
path == self.context || path.starts_with(&format!("{}/", self.context))
|
||||
path == self.r#in || path.starts_with(&format!("{}/", self.r#in))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ use super::Exclude;
|
|||
/// Configuration for file filtering
|
||||
#[derive(Debug, Deserialize, DeserializeOver2, Default)]
|
||||
pub struct Files {
|
||||
/// Enable git ignore integration
|
||||
#[serde(default)]
|
||||
pub gitignores: bool,
|
||||
|
||||
/// List of exclude rules with context-specific patterns
|
||||
#[serde(default)]
|
||||
pub excludes: Vec<Exclude>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ name = "yazi-fs"
|
|||
version = "25.9.15"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
authors = ["sxyazi <sxyazi@gmail.com>"]
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
description = "Yazi file system"
|
||||
homepage = "https://yazi-rs.github.io"
|
||||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
|
@ -20,9 +20,7 @@ arc-swap = "1.7.1"
|
|||
bitflags = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
foldhash = { workspace = true }
|
||||
git2 = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
ignore = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
percent-encoding = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
|
@ -36,11 +34,11 @@ libc = { workspace = true }
|
|||
uzers = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.61.2", features = ["Win32_Storage_FileSystem"] }
|
||||
windows-sys = { version = "0.61.2", features = [ "Win32_Storage_FileSystem" ] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation-sys = { workspace = true }
|
||||
objc = { workspace = true }
|
||||
objc2 = { workspace = true }
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
trash = "5.2.5"
|
||||
|
|
|
|||
|
|
@ -1,171 +1,46 @@
|
|||
use std::{collections::HashSet, path::{Path, PathBuf}, sync::Arc};
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use yazi_shared::url::AsUrl;
|
||||
|
||||
/// Filter for ignoring files based on git ignore rules and custom exclude
|
||||
/// patterns.
|
||||
/// Filter for ignoring files based on custom exclude patterns.
|
||||
///
|
||||
/// This filter combines git's native ignore functionality with custom exclude
|
||||
/// patterns that follow gitignore syntax. Exclude patterns can be
|
||||
/// context-specific and take precedence over git's ignore rules using the `!`
|
||||
/// prefix for negation.
|
||||
/// Exclude patterns can be context-specific and support negation using the `!`
|
||||
/// prefix for whitelisting.
|
||||
#[derive(Clone)]
|
||||
pub struct IgnoreFilter {
|
||||
/// Set of paths ignored by git (from git status)
|
||||
ignored_paths: HashSet<PathBuf>,
|
||||
/// Custom gitignore matcher for exclude patterns
|
||||
gitignore: Option<ignore::gitignore::Gitignore>,
|
||||
/// Custom glob-based matcher function for advanced pattern matching
|
||||
/// Returns Some(true) if should be ignored, Some(false) if whitelisted, None if no match
|
||||
/// Custom glob-based matcher function for pattern matching
|
||||
/// Returns Some(true) if should be ignored, Some(false) if whitelisted, None
|
||||
/// if no match
|
||||
glob_matcher: Option<Arc<dyn Fn(&Path) -> Option<bool> + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for IgnoreFilter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("IgnoreFilter")
|
||||
.field("ignored_paths", &self.ignored_paths)
|
||||
.field("gitignore", &self.gitignore)
|
||||
.field("glob_matcher", &self.glob_matcher.as_ref().map(|_| "Some(...)"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl IgnoreFilter {
|
||||
/// Creates a new `IgnoreFilter` by checking git ignore status for the given
|
||||
/// directory.
|
||||
/// Creates a new `IgnoreFilter` from exclude patterns.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dir` - Directory to check for git ignore status
|
||||
/// * `exclude_patterns` - Custom gitignore patterns that override git's rules
|
||||
/// * `use_git` - Whether to integrate with git ignore rules
|
||||
/// * `glob_matcher` - Custom glob-based matcher function for advanced pattern
|
||||
/// matching
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Some(IgnoreFilter)` if git repository is found (when use_git=true) or
|
||||
/// exclude patterns exist, `None` if no git repository and no exclude
|
||||
/// patterns.
|
||||
///
|
||||
/// # Behavior
|
||||
///
|
||||
/// - Discovers the git repository containing `dir` (if use_git=true)
|
||||
/// - Collects all ignored paths from git status
|
||||
/// - Builds a custom gitignore matcher from exclude patterns (relative to
|
||||
/// repo root)
|
||||
/// - Exclude patterns are checked first and take precedence over git ignore
|
||||
/// rules
|
||||
/// - Negation patterns (starting with `!`) can whitelist git-ignored files
|
||||
pub fn from_dir(
|
||||
dir: impl AsRef<Path>,
|
||||
exclude_patterns: &[String],
|
||||
use_git: bool,
|
||||
glob_matcher: Option<Arc<dyn Fn(&Path) -> Option<bool> + Send + Sync>>,
|
||||
) -> Option<Self> {
|
||||
let dir = dir.as_ref();
|
||||
|
||||
let (workdir, ignored_paths) = if use_git {
|
||||
// Try to open the git repository for this directory
|
||||
let repo = git2::Repository::discover(dir).ok()?;
|
||||
|
||||
// Get the workdir (root of the git repository)
|
||||
let workdir = repo.workdir()?.to_path_buf();
|
||||
|
||||
// Get git statuses for the repository
|
||||
let statuses = match repo.statuses(None) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
// Build a set of ALL ignored paths from git status
|
||||
let mut ignored_paths = HashSet::new();
|
||||
|
||||
// Manually add .git directory as ignored (like eza does)
|
||||
ignored_paths.insert(workdir.join(".git"));
|
||||
|
||||
// Add all ignored files from git status
|
||||
for status in statuses.iter() {
|
||||
if status.status() == git2::Status::IGNORED
|
||||
&& let Some(path) = status.path()
|
||||
{
|
||||
ignored_paths.insert(workdir.join(path));
|
||||
}
|
||||
}
|
||||
|
||||
(workdir, ignored_paths)
|
||||
} else {
|
||||
// No git integration, use current directory as base
|
||||
(dir.to_path_buf(), HashSet::new())
|
||||
};
|
||||
|
||||
// Build custom gitignore from exclude patterns if provided
|
||||
let gitignore = if !exclude_patterns.is_empty() {
|
||||
let mut builder = ignore::gitignore::GitignoreBuilder::new(&workdir);
|
||||
|
||||
// Add each exclude pattern
|
||||
for pattern in exclude_patterns {
|
||||
let _ = builder.add_line(None, pattern);
|
||||
}
|
||||
|
||||
builder.build().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if ignored_paths.is_empty()
|
||||
|| (ignored_paths.len() == 1 && ignored_paths.contains(&workdir.join(".git")))
|
||||
{
|
||||
// If we have no git-ignored paths but we have exclude patterns or glob matcher, still create
|
||||
// the filter
|
||||
if gitignore.is_some() || glob_matcher.is_some() {
|
||||
return Some(Self { ignored_paths, gitignore, glob_matcher });
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Store ALL ignored paths, not just the ones in the current directory
|
||||
// This way, when files are loaded later, we can check them against the full set
|
||||
Some(Self { ignored_paths, gitignore, glob_matcher })
|
||||
}
|
||||
|
||||
/// Creates a new `IgnoreFilter` from only exclude patterns without git
|
||||
/// integration.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dir` - Base directory for pattern matching
|
||||
/// * `patterns` - Gitignore-style patterns to apply
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Some(IgnoreFilter)` if patterns are provided, `None` if patterns is
|
||||
/// empty.
|
||||
///
|
||||
/// This is useful when `gitignores = false` but custom exclude patterns
|
||||
/// are still desired.
|
||||
/// `Some(IgnoreFilter)` if glob_matcher is provided, `None` otherwise.
|
||||
pub fn from_patterns(
|
||||
dir: impl AsRef<Path>,
|
||||
patterns: &[String],
|
||||
glob_matcher: Option<Arc<dyn Fn(&Path) -> Option<bool> + Send + Sync>>,
|
||||
) -> Option<Self> {
|
||||
if patterns.is_empty() && glob_matcher.is_none() {
|
||||
if glob_matcher.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let gitignore = if !patterns.is_empty() {
|
||||
let dir = dir.as_ref();
|
||||
let mut builder = ignore::gitignore::GitignoreBuilder::new(dir);
|
||||
|
||||
// Add each pattern
|
||||
for pattern in patterns {
|
||||
let _ = builder.add_line(None, pattern);
|
||||
}
|
||||
|
||||
builder.build().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(Self { ignored_paths: HashSet::new(), gitignore, glob_matcher })
|
||||
Some(Self { glob_matcher })
|
||||
}
|
||||
|
||||
/// Checks if a file should be ignored based on its URL.
|
||||
|
|
@ -180,83 +55,19 @@ impl IgnoreFilter {
|
|||
///
|
||||
/// # Matching Logic
|
||||
///
|
||||
/// 1. Check override patterns first:
|
||||
/// - If matched as whitelist (negation `!`), return `false` (not ignored)
|
||||
/// - If matched as ignore, return `true` (ignored)
|
||||
/// 2. Check if path is directly in git's ignored set
|
||||
/// 3. Check if any parent directory is ignored by git
|
||||
/// - If parent is ignored, also check if override patterns whitelist it
|
||||
/// - If parent is whitelisted, children are not ignored
|
||||
///
|
||||
/// This ensures override patterns take precedence and negation patterns work
|
||||
/// correctly even when navigating inside ignored directories.
|
||||
/// Uses the glob matcher to check if the path should be ignored.
|
||||
/// Returns true if matched as ignore, false if whitelisted or no match.
|
||||
pub fn matches_url(&self, url: impl AsUrl) -> bool {
|
||||
let url = url.as_url();
|
||||
let path = url.loc.as_path();
|
||||
|
||||
// First check glob matcher (highest priority)
|
||||
// Check glob matcher
|
||||
if let Some(ref matcher) = self.glob_matcher {
|
||||
if let Some(should_ignore) = matcher(path) {
|
||||
return should_ignore;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check if override patterns apply (they can negate ignores)
|
||||
// Override patterns take absolute priority
|
||||
if let Some(ref gitignore) = self.gitignore {
|
||||
let matched = gitignore.matched(path, path.is_dir());
|
||||
match matched {
|
||||
ignore::Match::None => {
|
||||
// No override pattern matched, fall through to git ignore check
|
||||
}
|
||||
ignore::Match::Ignore(_) => {
|
||||
// Override pattern says to ignore
|
||||
return true;
|
||||
}
|
||||
ignore::Match::Whitelist(_) => {
|
||||
// Override pattern says NOT to ignore (negation pattern like !target/)
|
||||
// This takes precedence over git ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check exact match first
|
||||
if self.ignored_paths.contains(path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any parent directory is ignored
|
||||
// BUT also check if parent is whitelisted by override patterns
|
||||
let mut current = path;
|
||||
while let Some(parent) = current.parent() {
|
||||
if self.ignored_paths.contains(parent) {
|
||||
// Parent is ignored by git, but check if override patterns whitelist it
|
||||
if let Some(ref gitignore) = self.gitignore {
|
||||
let matched = gitignore.matched(parent, true); // parent is always a directory
|
||||
if matches!(matched, ignore::Match::Whitelist(_)) {
|
||||
// Parent is whitelisted, so children should not be ignored
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if a path should be ignored
|
||||
pub fn matches_path(&self, path: &Path) -> bool { self.ignored_paths.contains(path) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_git_repo_discovery() {
|
||||
// This test assumes we're running in the yazi git repo
|
||||
let current_dir = std::env::current_dir().unwrap();
|
||||
assert!(git2::Repository::discover(¤t_dir).is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue