mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Fix plugin mechanic to follow Yazi directory caching.
This commit is contained in:
parent
cb11bdbe31
commit
e32db2a539
5 changed files with 197 additions and 22 deletions
|
|
@ -48,19 +48,34 @@ impl Actor for Cd {
|
|||
}
|
||||
|
||||
// Current
|
||||
let rep = tab.history.remove_or(&opt.target);
|
||||
let mut rep = tab.history.remove_or(&opt.target);
|
||||
|
||||
// Only force reload if folder doesn't have cached ignore filters
|
||||
// If filters are cached, we can reuse the folder as-is (files are already
|
||||
// filtered) This avoids the race condition where cached unfiltered files
|
||||
// appear before plugin runs
|
||||
if rep.files.ignore_filter().is_none() {
|
||||
rep.cha = Default::default();
|
||||
rep.files.update_ioerr();
|
||||
rep.stage = Default::default();
|
||||
}
|
||||
let rep = mem::replace(&mut tab.current, rep);
|
||||
tab.history.insert(rep.url.to_owned(), rep);
|
||||
|
||||
// Parent
|
||||
if let Some(parent) = opt.target.parent() {
|
||||
tab.parent = Some(tab.history.remove_or(parent));
|
||||
let mut parent_folder = tab.history.remove_or(parent);
|
||||
// Only force parent reload if it doesn't have cached filters
|
||||
if parent_folder.files.ignore_filter().is_none() {
|
||||
parent_folder.cha = Default::default();
|
||||
parent_folder.files.update_ioerr();
|
||||
parent_folder.stage = Default::default();
|
||||
}
|
||||
tab.parent = Some(parent_folder);
|
||||
}
|
||||
|
||||
err!(Pubsub::pub_after_cd(tab.id, tab.cwd()));
|
||||
act!(mgr:hidden, cx)?;
|
||||
act!(mgr:sort, cx)?;
|
||||
act!(mgr:ignore, cx)?;
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:refresh, cx)?;
|
||||
succ!(render!());
|
||||
|
|
|
|||
|
|
@ -31,6 +31,21 @@ impl Actor for ExcludeAdd {
|
|||
cwd.as_path().map(|p| p.display().to_string()).unwrap_or_default()
|
||||
};
|
||||
|
||||
// Check if the current folder itself is matched by any of the patterns
|
||||
// If so, don't apply the filter - we're viewing inside a gitignored directory
|
||||
if let Some(_cwd_path) = cwd.as_path() {
|
||||
// Build a quick GlobSet to test if current folder matches any pattern
|
||||
let mut test_builder = GlobSetBuilder::new();
|
||||
for pattern in &opt.patterns {
|
||||
if pattern.starts_with('!') {
|
||||
// Skip negation patterns for this test
|
||||
continue;
|
||||
} else if let Ok(glob) = Glob::new(pattern) {
|
||||
test_builder.add(glob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get existing patterns from config
|
||||
let config_patterns = YAZI.files.excludes_for_context(&cwd_str);
|
||||
|
||||
|
|
@ -123,8 +138,92 @@ impl Actor for ExcludeAdd {
|
|||
}
|
||||
};
|
||||
|
||||
// Apply to CWD
|
||||
if apply(cx.current_mut(), ignore_filter.clone()) {
|
||||
// Apply to CWD and parent
|
||||
let cwd_changed = apply(cx.current_mut(), ignore_filter.clone());
|
||||
|
||||
let parent_changed = if let Some(p) = cx.parent_mut() {
|
||||
let parent_str = if p.url.is_search() {
|
||||
"search://**".to_string()
|
||||
} else {
|
||||
p.url.as_path().map(|p| p.display().to_string()).unwrap_or_default()
|
||||
};
|
||||
|
||||
let parent_config_patterns = YAZI.files.excludes_for_context(&parent_str);
|
||||
let mut parent_all_patterns = opt.patterns.clone();
|
||||
parent_all_patterns.extend(parent_config_patterns);
|
||||
|
||||
// Compile glob patterns for parent (same as CWD)
|
||||
let mut parent_ignores_builder = GlobSetBuilder::new();
|
||||
let mut parent_whitelists_builder = GlobSetBuilder::new();
|
||||
|
||||
for pattern in &parent_all_patterns {
|
||||
if let Some(negated) = pattern.strip_prefix('!') {
|
||||
if let Ok(glob) = Glob::new(negated) {
|
||||
parent_whitelists_builder.add(glob);
|
||||
}
|
||||
} else if let Ok(glob) = Glob::new(pattern) {
|
||||
parent_ignores_builder.add(glob);
|
||||
}
|
||||
}
|
||||
|
||||
let parent_ignores = parent_ignores_builder.build().ok();
|
||||
let parent_whitelists = parent_whitelists_builder.build().ok();
|
||||
|
||||
let parent_matcher: Option<Arc<dyn Fn(&std::path::Path) -> Option<bool> + Send + Sync>> =
|
||||
if parent_ignores.is_some() || parent_whitelists.is_some() {
|
||||
let context = parent_str.clone();
|
||||
Some(Arc::new(move |path: &std::path::Path| {
|
||||
// First check config patterns (for user overrides/negation)
|
||||
if let Some(result) = YAZI.files.matches_path(path, &context) {
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
// For absolute paths, try both the full path and relative components
|
||||
let paths_to_check: Vec<&std::path::Path> = if path.is_absolute() {
|
||||
let mut paths = vec![path];
|
||||
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(std::path::Path::new(subpath));
|
||||
}
|
||||
}
|
||||
}
|
||||
paths
|
||||
} else {
|
||||
vec![path]
|
||||
};
|
||||
|
||||
// Check whitelist first (negation takes precedence)
|
||||
if let Some(ref wl) = parent_whitelists {
|
||||
for p in &paths_to_check {
|
||||
if wl.is_match(p) {
|
||||
return Some(false); // Explicitly NOT ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check ignore patterns
|
||||
if let Some(ref ig) = parent_ignores {
|
||||
for p in &paths_to_check {
|
||||
if ig.is_match(p) {
|
||||
return Some(true); // Should be ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let parent_filter = IgnoreFilter::from_patterns(parent_matcher);
|
||||
apply(p, parent_filter)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if cwd_changed || parent_changed {
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:update_paged, cx)?;
|
||||
}
|
||||
|
|
@ -141,23 +240,65 @@ impl Actor for ExcludeAdd {
|
|||
let mut hovered_all_patterns = opt.patterns;
|
||||
hovered_all_patterns.extend(hovered_config_patterns);
|
||||
|
||||
// Compile glob patterns for hovered (same as CWD)
|
||||
let mut hovered_ignores_builder = GlobSetBuilder::new();
|
||||
let mut hovered_whitelists_builder = GlobSetBuilder::new();
|
||||
|
||||
for pattern in &hovered_all_patterns {
|
||||
if let Some(negated) = pattern.strip_prefix('!') {
|
||||
if let Ok(glob) = Glob::new(negated) {
|
||||
hovered_whitelists_builder.add(glob);
|
||||
}
|
||||
} else if let Ok(glob) = Glob::new(pattern) {
|
||||
hovered_ignores_builder.add(glob);
|
||||
}
|
||||
}
|
||||
|
||||
let hovered_ignores = hovered_ignores_builder.build().ok();
|
||||
let hovered_whitelists = hovered_whitelists_builder.build().ok();
|
||||
|
||||
let hovered_matcher: Option<Arc<dyn Fn(&std::path::Path) -> Option<bool> + Send + Sync>> =
|
||||
if !hovered_all_patterns.is_empty() {
|
||||
if hovered_ignores.is_some() || hovered_whitelists.is_some() {
|
||||
let context = hovered_str.clone();
|
||||
let patterns = hovered_all_patterns.clone();
|
||||
Some(Arc::new(move |path: &std::path::Path| {
|
||||
// First check config patterns (for user overrides/negation)
|
||||
if let Some(result) = YAZI.files.matches_path(path, &context) {
|
||||
return Some(result);
|
||||
}
|
||||
for pattern in &patterns {
|
||||
if let Some(negated) = pattern.strip_prefix('!') {
|
||||
if path.to_str().map_or(false, |p| p.contains(negated)) {
|
||||
return Some(false);
|
||||
|
||||
// For absolute paths, try both the full path and relative components
|
||||
let paths_to_check: Vec<&std::path::Path> = if path.is_absolute() {
|
||||
let mut paths = vec![path];
|
||||
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(std::path::Path::new(subpath));
|
||||
}
|
||||
}
|
||||
}
|
||||
paths
|
||||
} else {
|
||||
vec![path]
|
||||
};
|
||||
|
||||
// Check whitelist first (negation takes precedence)
|
||||
if let Some(ref wl) = hovered_whitelists {
|
||||
for p in &paths_to_check {
|
||||
if wl.is_match(p) {
|
||||
return Some(false); // Explicitly NOT ignored
|
||||
}
|
||||
} else if path.to_str().map_or(false, |p| p.contains(pattern)) {
|
||||
return Some(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check ignore patterns
|
||||
if let Some(ref ig) = hovered_ignores {
|
||||
for p in &paths_to_check {
|
||||
if ig.is_match(p) {
|
||||
return Some(true); // Should be ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,32 @@ impl Actor for Ignore {
|
|||
}
|
||||
};
|
||||
|
||||
// Apply to CWD
|
||||
if apply(cx.current_mut(), ignore_filter.clone()) {
|
||||
// Apply to CWD and parent
|
||||
let cwd_changed = apply(cx.current_mut(), ignore_filter.clone());
|
||||
|
||||
let parent_changed = if let Some(p) = cx.parent_mut() {
|
||||
let parent_str = if p.url.is_search() {
|
||||
"search://**".to_string()
|
||||
} else {
|
||||
p.url.as_path().map(|p| p.display().to_string()).unwrap_or_default()
|
||||
};
|
||||
|
||||
let parent_excludes = YAZI.files.excludes_for_context(&parent_str);
|
||||
let parent_filter = if !parent_excludes.is_empty() {
|
||||
let context = parent_str.clone();
|
||||
let matcher: Option<Arc<dyn Fn(&std::path::Path) -> Option<bool> + Send + Sync>> =
|
||||
Some(Arc::new(move |path: &std::path::Path| YAZI.files.matches_path(path, &context)));
|
||||
IgnoreFilter::from_patterns(matcher)
|
||||
} else {
|
||||
IgnoreFilter::from_patterns(None)
|
||||
};
|
||||
|
||||
apply(p, parent_filter)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if cwd_changed || parent_changed {
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:update_paged, cx)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ impl Actor for Refresh {
|
|||
execute!(TTY.writer(), SetTitle(s)).ok();
|
||||
}
|
||||
|
||||
// Apply ignore filter before triggering file loads
|
||||
act!(mgr:ignore, cx)?;
|
||||
|
||||
if let Some(p) = cx.parent() {
|
||||
Self::trigger_dirs(&[cx.current(), p]);
|
||||
} else {
|
||||
|
|
@ -53,7 +50,6 @@ impl Refresh {
|
|||
fn trigger_dirs(folders: &[&Folder]) {
|
||||
async fn go(cwd: UrlBuf, cha: Cha) {
|
||||
let Some(cha) = Files::assert_stale(&cwd, cha).await else { return };
|
||||
|
||||
match Files::from_dir_bulk(&cwd).await {
|
||||
Ok(files) => FilesOp::Full(cwd, files, cha).emit(),
|
||||
Err(e) => FilesOp::issue_error(&cwd, e).await,
|
||||
|
|
@ -65,7 +61,6 @@ impl Refresh {
|
|||
.filter(|&f| f.url.is_internal())
|
||||
.map(|&f| go(f.url.to_owned(), f.cha))
|
||||
.collect();
|
||||
|
||||
if !futs.is_empty() {
|
||||
tokio::spawn(futures::future::join_all(futs));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ ueberzug_scale = 1
|
|||
ueberzug_offset = [ 0, 0, 0, 0 ]
|
||||
|
||||
|
||||
[files]
|
||||
# [files]
|
||||
# Context-specific exclude patterns
|
||||
# Patterns are compiled into glob matchers for efficient matching
|
||||
# Patterns starting with '!' negate (whitelist) previous matches
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue