Compare commits

...

2 Commits

Author SHA1 Message Date
Dmitriy Kovalenko 3af1cda8a6 fix: Format code 2026-05-20 10:33:51 -07:00
Dmitriy Kovalenko 31bcc61bcc fix: Add allowlist and denylist to FilePickerOptions (#448)
Refs #448
2026-05-20 10:32:54 -07:00
4 changed files with 88 additions and 0 deletions
+4
View File
@@ -270,6 +270,8 @@ pub unsafe extern "C" fn fff_create_instance2(
watch,
mode,
cache_budget,
allowlist: Vec::new(),
denylist: Vec::new(),
},
) {
return FffResult::err(&format!("Failed to init file picker: {}", e));
@@ -924,6 +926,8 @@ pub unsafe extern "C" fn fff_restart_index(
watch,
mode,
cache_budget: None,
allowlist: Vec::new(),
denylist: Vec::new(),
},
) {
Ok(()) => FffResult::ok_empty(),
+68
View File
@@ -444,6 +444,10 @@ pub struct FilePickerOptions {
pub cache_budget: Option<ContentCacheBudget>,
/// When `false`, `new_with_shared_state` skips the background file watcher.
pub watch: bool,
/// Glob patterns to include during indexing. Empty = include all.
pub allowlist: Vec<String>,
/// Glob patterns to exclude during indexing. Applied after allowlist.
pub denylist: Vec<String>,
}
impl Default for FilePickerOptions {
@@ -455,6 +459,8 @@ impl Default for FilePickerOptions {
mode: FFFMode::default(),
cache_budget: None,
watch: true,
allowlist: Vec::new(),
denylist: Vec::new(),
}
}
}
@@ -471,6 +477,8 @@ pub struct FilePicker {
enable_mmap_cache: bool,
enable_content_indexing: bool,
watch: bool,
allowlist: Vec<String>,
denylist: Vec<String>,
}
impl std::fmt::Debug for FilePicker {
@@ -528,6 +536,14 @@ impl FilePicker {
self.mode
}
pub fn allowlist(&self) -> &[String] {
&self.allowlist
}
pub fn denylist(&self) -> &[String] {
&self.denylist
}
pub fn cache_budget(&self) -> &ContentCacheBudget {
&self.cache_budget
}
@@ -717,6 +733,8 @@ impl FilePicker {
enable_mmap_cache: options.enable_mmap_cache,
enable_content_indexing: options.enable_content_indexing,
watch: options.watch,
allowlist: options.allowlist,
denylist: options.denylist,
})
}
@@ -745,6 +763,8 @@ impl FilePicker {
let signals = picker.scan_signals();
let scanned_files_counter = picker.scanned_files_counter();
let path = picker.base_path.clone();
let allowlist = picker.allowlist.clone();
let denylist = picker.denylist.clone();
{
let mut guard = shared_picker.write()?;
@@ -768,6 +788,8 @@ impl FilePicker {
auto_cache_budget: true,
install_watcher: true,
},
allowlist,
denylist,
)
.spawn();
@@ -796,6 +818,8 @@ impl FilePicker {
&self.scanned_files_count,
&empty_frecency,
self.mode,
&self.allowlist,
&self.denylist,
)?;
self.sync_data = sync;
@@ -1711,12 +1735,40 @@ impl FileSync {
synced_files_count: &Arc<AtomicUsize>,
shared_frecency: &SharedFrecency,
mode: FFFMode,
allowlist: &[String],
denylist: &[String],
) -> Result<FileSync, Error> {
use globset::{Glob, GlobSetBuilder};
use ignore::WalkBuilder;
let scan_start = std::time::Instant::now();
info!("SCAN: Starting filesystem walk and git status (async)");
// Build globsets for filtering
let allowlist_set = if !allowlist.is_empty() {
let mut builder = GlobSetBuilder::new();
for pattern in allowlist {
if let Ok(glob) = Glob::new(pattern) {
builder.add(glob);
}
}
builder.build().ok()
} else {
None
};
let denylist_set = if !denylist.is_empty() {
let mut builder = GlobSetBuilder::new();
for pattern in denylist {
if let Ok(glob) = Glob::new(pattern) {
builder.add(glob);
}
}
builder.build().ok()
} else {
None
};
// Walk files (the fast part, typically 2-3s even on huge repos).
let is_git_repo = git_workdir.is_some();
let bg_threads = BACKGROUND_THREAD_POOL.current_num_threads();
@@ -1749,6 +1801,8 @@ impl FileSync {
let pairs = &pairs;
let counter = Arc::clone(synced_files_count);
let base_path = base_path.to_path_buf();
let allowlist_set = allowlist_set.clone();
let denylist_set = denylist_set.clone();
Box::new(move |result| {
let Ok(entry) = result else {
@@ -1768,6 +1822,20 @@ impl FileSync {
return ignore::WalkState::Continue;
}
// Apply allowlist/denylist filtering
if let Ok(rel_path) = path.strip_prefix(&base_path) {
if let Some(ref allow_set) = allowlist_set {
if !allow_set.is_match(rel_path) {
return ignore::WalkState::Continue;
}
}
if let Some(ref deny_set) = denylist_set {
if deny_set.is_match(rel_path) {
return ignore::WalkState::Continue;
}
}
}
let metadata = entry.metadata().ok();
let (file_item, rel_path) =
FileItem::new_from_walk(path, &base_path, None, metadata.as_ref());
+14
View File
@@ -57,6 +57,8 @@ pub(crate) struct ScanJob {
/// side. Reset to 0 at scan start, incremented per-file by the
/// walker. Shared `Arc` so the UI polls the same atomic.
scanned_files_counter: Arc<AtomicUsize>,
allowlist: Vec<String>,
denylist: Vec<String>,
}
impl ScanJob {
@@ -80,6 +82,8 @@ impl ScanJob {
let signals = picker.scan_signals();
let scanned_files_counter = picker.scanned_files_counter();
let base_path = picker.base_path().to_path_buf();
let allowlist = picker.allowlist().to_vec();
let denylist = picker.denylist().to_vec();
let new_scan_config = ScanConfig {
warmup: picker.has_mmap_cache(),
@@ -99,6 +103,8 @@ impl ScanJob {
config: new_scan_config,
shared_picker: shared_picker.clone(),
shared_frecency: shared_frecency.clone(),
allowlist,
denylist,
}))
}
@@ -110,6 +116,8 @@ impl ScanJob {
signals: ScanSignals,
scanned_files_counter: Arc<AtomicUsize>,
config: ScanConfig,
allowlist: Vec<String>,
denylist: Vec<String>,
) -> Self {
Self {
shared_picker,
@@ -119,6 +127,8 @@ impl ScanJob {
signals,
scanned_files_counter,
config,
allowlist,
denylist,
}
}
@@ -140,6 +150,8 @@ impl ScanJob {
signals,
scanned_files_counter,
config,
allowlist,
denylist,
} = self;
let _scanning = ScanningGuard::new(&signals, config.install_watcher);
@@ -157,6 +169,8 @@ impl ScanJob {
&scanned_files_counter,
&shared_frecency,
mode,
&allowlist,
&denylist,
) {
Ok(sync) => sync,
Err(e) => {
+2
View File
@@ -269,6 +269,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
cache_budget: args
.max_cached_files
.map(fff::ContentCacheBudget::new_for_repo),
allowlist: Vec::new(),
denylist: Vec::new(),
},
)
.map_err(|e| format!("Failed to init file picker: {}", e))?;