Skip to content

Commit 5060af1

Browse files
committed
breaking(sdk): Rename several options & add more flags to control
1 parent e2ce56a commit 5060af1

29 files changed

Lines changed: 217 additions & 91 deletions

crates/fff-c/include/fff.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,14 @@ typedef struct FffMixedSearchResult {
340340
*
341341
* # Parameters
342342
*
343-
* * `base_path` – directory to index (required)
344-
* * `frecency_db_path` – path to frecency LMDB database (NULL/empty to skip)
345-
* * `history_db_path` – path to query history LMDB database (NULL/empty to skip)
346-
* * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
347-
* * `warmup_mmap_cache` – pre-populate mmap caches after the initial scan
348-
* * `ai_mode` – enable AI-agent optimizations (auto-track frecency on modifications)
343+
* * `base_path` – directory to index (required)
344+
* * `frecency_db_path` – path to frecency LMDB database (NULL/empty to skip)
345+
* * `history_db_path` – path to query history LMDB database (NULL/empty to skip)
346+
* * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
347+
* * `enable_mmap_cache` – pre-populate mmap caches after the initial scan
348+
* * `enable_content_indexing` – build content index after the initial scan
349+
* * `watch` – start a background file-system watcher for live updates
350+
* * `ai_mode` – enable AI-agent optimizations (auto-track frecency on modifications)
349351
*
350352
* ## Safety
351353
* String parameters must be valid null-terminated UTF-8 or NULL.
@@ -354,7 +356,9 @@ struct FffResult *fff_create_instance(const char *base_path,
354356
const char *frecency_db_path,
355357
const char *history_db_path,
356358
bool use_unsafe_no_lock,
357-
bool warmup_mmap_cache,
359+
bool enable_mmap_cache,
360+
bool enable_content_indexing,
361+
bool watch,
358362
bool ai_mode);
359363

360364
/**

crates/fff-c/src/lib.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,14 @@ fn default_i32(val: i32, default: i32) -> i32 {
110110
///
111111
/// # Parameters
112112
///
113-
/// * `base_path` – directory to index (required)
114-
/// * `frecency_db_path` – path to frecency LMDB database (NULL/empty to skip)
115-
/// * `history_db_path` – path to query history LMDB database (NULL/empty to skip)
116-
/// * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
117-
/// * `warmup_mmap_cache` – pre-populate mmap caches after the initial scan
118-
/// * `ai_mode` – enable AI-agent optimizations (auto-track frecency on modifications)
113+
/// * `base_path` – directory to index (required)
114+
/// * `frecency_db_path` – path to frecency LMDB database (NULL/empty to skip)
115+
/// * `history_db_path` – path to query history LMDB database (NULL/empty to skip)
116+
/// * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
117+
/// * `enable_mmap_cache` – pre-populate mmap caches after the initial scan
118+
/// * `enable_content_indexing` – build content index after the initial scan
119+
/// * `watch` – start a background file-system watcher for live updates
120+
/// * `ai_mode` – enable AI-agent optimizations (auto-track frecency on modifications)
119121
///
120122
/// ## Safety
121123
/// String parameters must be valid null-terminated UTF-8 or NULL.
@@ -125,7 +127,9 @@ pub unsafe extern "C" fn fff_create_instance(
125127
frecency_db_path: *const c_char,
126128
history_db_path: *const c_char,
127129
use_unsafe_no_lock: bool,
128-
warmup_mmap_cache: bool,
130+
enable_mmap_cache: bool,
131+
enable_content_indexing: bool,
132+
watch: bool,
129133
ai_mode: bool,
130134
) -> *mut FffResult {
131135
let base_path_str = match unsafe { cstr_to_str(base_path) } {
@@ -186,10 +190,11 @@ pub unsafe extern "C" fn fff_create_instance(
186190
shared_frecency.clone(),
187191
fff::FilePickerOptions {
188192
base_path: base_path_str,
189-
warmup_mmap_cache,
193+
enable_mmap_cache,
194+
enable_content_indexing,
195+
watch,
190196
mode,
191197
cache_budget: None,
192-
..Default::default()
193198
},
194199
) {
195200
return FffResult::err(&format!("Failed to init file picker: {}", e));
@@ -829,13 +834,15 @@ pub unsafe extern "C" fn fff_restart_index(
829834
Err(e) => return FffResult::err(&format!("Failed to acquire file picker lock: {}", e)),
830835
};
831836

832-
let (warmup_caches, mode) = if let Some(mut picker) = guard.take() {
833-
let warmup = picker.need_warmup_mmap_cache();
837+
let (warmup_caches, content_indexing, watch, mode) = if let Some(mut picker) = guard.take() {
838+
let warmup = picker.need_enable_mmap_cache();
839+
let ci = picker.need_enable_content_indexing();
840+
let w = picker.need_watch();
834841
let mode = picker.mode();
835842
picker.stop_background_monitor();
836-
(warmup, mode)
843+
(warmup, ci, w, mode)
837844
} else {
838-
(false, FFFMode::default())
845+
(false, false, true, FFFMode::default())
839846
};
840847

841848
drop(guard);
@@ -845,10 +852,11 @@ pub unsafe extern "C" fn fff_restart_index(
845852
inst.frecency.clone(),
846853
fff::FilePickerOptions {
847854
base_path: canonical_path.to_string_lossy().to_string(),
848-
warmup_mmap_cache: warmup_caches,
855+
enable_mmap_cache: warmup_caches,
856+
enable_content_indexing: content_indexing,
857+
watch,
849858
mode,
850859
cache_budget: None,
851-
..Default::default()
852860
},
853861
) {
854862
Ok(()) => FffResult::ok_empty(),

crates/fff-core/src/background_watcher.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,9 @@ fn trigger_full_rescan(shared_picker: &SharedPicker, shared_frecency: &SharedFre
560560
// Spawn background warmup + bigram rebuild (mirrors the initial scan's
561561
// post-scan phase). The write lock is still held here but the spawned
562562
// thread re-acquires it later — safe because the guard drops at function end.
563-
picker.spawn_post_rescan_rebuild(shared_picker.clone());
563+
if shared_picker.need_complex_rebuild() {
564+
picker.spawn_post_rescan_rebuild(shared_picker.clone());
565+
}
564566
}
565567

566568
/// After registering a watch on a newly created directory, list its

crates/fff-core/src/file_picker.rs

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -389,21 +389,25 @@ impl FileItem {
389389
/// Options for creating a [`FilePicker`].
390390
pub struct FilePickerOptions {
391391
pub base_path: String,
392-
pub warmup_mmap_cache: bool,
392+
/// Pre-populate mmap caches for top-frecency files after the initial scan.
393+
pub enable_mmap_cache: bool,
394+
/// Build content index after the initial scan for faster content-aware filtering.
395+
pub enable_content_indexing: bool,
396+
/// Mode of the picker impact the way file watcher events are handled and the scoring logic
393397
pub mode: FFFMode,
394398
/// Explicit cache budget. When `None`, the budget is auto-computed from
395399
/// the repo size after the initial scan completes.
396400
pub cache_budget: Option<ContentCacheBudget>,
397401
/// When `false`, `new_with_shared_state` skips the background file watcher.
398-
/// Files are still scanned, warmed up, and bigram-indexed.
399402
pub watch: bool,
400403
}
401404

402405
impl Default for FilePickerOptions {
403406
fn default() -> Self {
404407
Self {
405408
base_path: ".".into(),
406-
warmup_mmap_cache: false,
409+
enable_mmap_cache: false,
410+
enable_content_indexing: false,
407411
mode: FFFMode::default(),
408412
cache_budget: None,
409413
watch: true,
@@ -421,7 +425,8 @@ pub struct FilePicker {
421425
watcher_ready: Arc<AtomicBool>,
422426
scanned_files_count: Arc<AtomicUsize>,
423427
background_watcher: Option<BackgroundWatcher>,
424-
warmup_mmap_cache: bool,
428+
enable_mmap_cache: bool,
429+
enable_content_indexing: bool,
425430
watch: bool,
426431
cancelled: Arc<AtomicBool>,
427432
// This is a soft lock that we use to prevent rescan be triggered while the
@@ -479,8 +484,16 @@ impl FilePicker {
479484
.and_then(|p| p.to_str())
480485
}
481486

482-
pub fn need_warmup_mmap_cache(&self) -> bool {
483-
self.warmup_mmap_cache
487+
pub fn need_enable_mmap_cache(&self) -> bool {
488+
self.enable_mmap_cache
489+
}
490+
491+
pub fn need_enable_content_indexing(&self) -> bool {
492+
self.enable_content_indexing
493+
}
494+
495+
pub fn need_watch(&self) -> bool {
496+
self.watch
484497
}
485498

486499
pub fn mode(&self) -> FFFMode {
@@ -632,7 +645,8 @@ impl FilePicker {
632645
post_scan_busy: Arc::new(AtomicBool::new(false)),
633646
scanned_files_count: Arc::new(AtomicUsize::new(0)),
634647
sync_data: FileSync::new(),
635-
warmup_mmap_cache: options.warmup_mmap_cache,
648+
enable_mmap_cache: options.enable_mmap_cache,
649+
enable_content_indexing: options.enable_content_indexing,
636650
watch: options.watch,
637651
watcher_ready: Arc::new(AtomicBool::new(false)),
638652
})
@@ -648,13 +662,15 @@ impl FilePicker {
648662
let picker = Self::new(options)?;
649663

650664
info!(
651-
"Spawning background threads: base_path={}, warmup={}, mode={:?}",
665+
"Spawning background threads: base_path={}, warmup={}, content_indexing={}, mode={:?}",
652666
picker.base_path.display(),
653-
picker.warmup_mmap_cache,
667+
picker.enable_mmap_cache,
668+
picker.enable_content_indexing,
654669
picker.mode,
655670
);
656671

657-
let warmup = picker.warmup_mmap_cache;
672+
let warmup = picker.enable_mmap_cache;
673+
let content_indexing = picker.enable_content_indexing;
658674
let watch = picker.watch;
659675
let mode = picker.mode;
660676

@@ -678,6 +694,7 @@ impl FilePicker {
678694
watcher_ready,
679695
synced_files_count,
680696
warmup,
697+
content_indexing,
681698
watch,
682699
mode,
683700
shared_picker,
@@ -1400,13 +1417,15 @@ impl FilePicker {
14001417

14011418
/// Spawn a background thread to rebuild the bigram index after rescan.
14021419
pub(crate) fn spawn_post_rescan_rebuild(&self, shared_picker: SharedPicker) -> bool {
1403-
if !self.warmup_mmap_cache || self.cancelled.load(Ordering::Relaxed) {
1420+
if self.cancelled.load(Ordering::Relaxed) {
14041421
return false;
14051422
}
14061423

14071424
let post_scan_busy = Arc::clone(&self.post_scan_busy);
14081425
let cancelled = Arc::clone(&self.cancelled);
14091426
let auto_budget = !self.has_explicit_cache_budget;
1427+
let do_warmup = self.enable_mmap_cache;
1428+
let do_content_indexing = self.enable_content_indexing;
14101429

14111430
post_scan_busy.store(true, Ordering::Release);
14121431

@@ -1450,7 +1469,7 @@ impl FilePicker {
14501469

14511470
if let Some((files, budget, bp, arena)) = files_snapshot {
14521471
// Warmup mmap caches.
1453-
if !cancelled.load(Ordering::Acquire) {
1472+
if do_warmup && !cancelled.load(Ordering::Acquire) {
14541473
let t = std::time::Instant::now();
14551474
warmup_mmaps(files, &budget, &bp, arena);
14561475
info!(
@@ -1462,7 +1481,7 @@ impl FilePicker {
14621481
}
14631482

14641483
// Build bigram index (lock-free).
1465-
if !cancelled.load(Ordering::Acquire) {
1484+
if do_content_indexing && !cancelled.load(Ordering::Acquire) {
14661485
let t = std::time::Instant::now();
14671486
info!(
14681487
"Rescan: starting bigram index build for {} files...",
@@ -1495,8 +1514,10 @@ impl FilePicker {
14951514

14961515
post_scan_busy.store(false, Ordering::Release);
14971516
info!(
1498-
"Rescan post-scan warmup + bigram total: {:.2}s",
1517+
"Rescan post-scan phase total: {:.2}s (warmup={}, content_indexing={})",
14991518
phase_start.elapsed().as_secs_f64(),
1519+
do_warmup,
1520+
do_content_indexing,
15001521
);
15011522
});
15021523

@@ -1617,7 +1638,8 @@ fn spawn_scan_and_watcher(
16171638
scan_signal: Arc<AtomicBool>,
16181639
watcher_ready: Arc<AtomicBool>,
16191640
synced_files_count: Arc<AtomicUsize>,
1620-
warmup_mmap_cache: bool,
1641+
enable_mmap_cache: bool,
1642+
enable_content_indexing: bool,
16211643
watch: bool,
16221644
mode: FFFMode,
16231645
shared_picker: SharedPicker,
@@ -1725,7 +1747,10 @@ fn spawn_scan_and_watcher(
17251747

17261748
watcher_ready.store(true, Ordering::Release);
17271749

1728-
if warmup_mmap_cache && !cancelled.load(Ordering::Acquire) {
1750+
let need_post_scan =
1751+
(enable_mmap_cache || enable_content_indexing) && !cancelled.load(Ordering::Acquire);
1752+
1753+
if need_post_scan {
17291754
post_scan_busy.store(true, Ordering::Release);
17301755
let phase_start = std::time::Instant::now();
17311756

@@ -1768,9 +1793,11 @@ fn spawn_scan_and_watcher(
17681793
None
17691794
};
17701795

1796+
// both of this is using a custom soft lock not guaranteed by compiler
1797+
// this is required to keep the picker functioning if someone opened a really crazy
1798+
// e.g 10m files directory but potentially unsafe
17711799
if let Some((files, budget, arena)) = files_snapshot {
1772-
// Warmup: populate mmap caches for top-frecency files.
1773-
if !cancelled.load(Ordering::Acquire) {
1800+
if enable_mmap_cache && !cancelled.load(Ordering::Acquire) {
17741801
let warmup_start = std::time::Instant::now();
17751802
warmup_mmaps(files, &budget, &base_path, arena);
17761803
info!(
@@ -1781,16 +1808,9 @@ fn spawn_scan_and_watcher(
17811808
);
17821809
}
17831810

1784-
// Build bigram index — entirely lock-free.
1785-
if !cancelled.load(Ordering::Acquire) {
1786-
let bigram_start = std::time::Instant::now();
1787-
info!("Starting bigram index build for {} files...", files.len());
1811+
if enable_content_indexing && !cancelled.load(Ordering::Acquire) {
17881812
let (index, content_binary) =
17891813
build_bigram_index(files, &budget, &base_path, arena);
1790-
info!(
1791-
"Bigram index ready in {:.2}s",
1792-
bigram_start.elapsed().as_secs_f64(),
1793-
);
17941814

17951815
if let Ok(mut guard) = shared_picker.write()
17961816
&& let Some(ref mut picker) = *guard
@@ -1813,8 +1833,10 @@ fn spawn_scan_and_watcher(
18131833
post_scan_busy.store(false, Ordering::Release);
18141834

18151835
info!(
1816-
"Post-scan warmup + bigram total: {:.2}s",
1836+
"Post-scan phase total: {:.2}s (warmup={}, content_indexing={})",
18171837
phase_start.elapsed().as_secs_f64(),
1838+
enable_mmap_cache,
1839+
enable_content_indexing,
18181840
);
18191841
}
18201842

@@ -1897,6 +1919,7 @@ pub(crate) fn warmup_mmaps(
18971919
/// so reading further adds no new information to the index.
18981920
pub const BIGRAM_CONTENT_CAP: usize = 64 * 1024;
18991921

1922+
#[tracing::instrument(skip_all, name = "Building Bigram Index", level = Level::DEBUG)]
19001923
pub(crate) fn build_bigram_index(
19011924
files: &[FileItem],
19021925
budget: &ContentCacheBudget,

crates/fff-core/src/shared.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ impl SharedPicker {
3434
Ok(self.0.write())
3535
}
3636

37+
/// Return `true` if this is an instance of the picker that requires a complicated post-scan
38+
/// indexing/cache warmup job. The indexing is not crazy but it takes time.
39+
pub fn need_complex_rebuild(&self) -> bool {
40+
let guard = self.0.read();
41+
guard
42+
.as_ref()
43+
.is_some_and(|p| p.need_enable_mmap_cache() || p.need_enable_content_indexing())
44+
}
45+
3746
/// Block until the background filesystem scan finishes.
3847
/// Returns `true` if scan completed, `false` on timeout.
3948
pub fn wait_for_scan(&self, timeout: Duration) -> bool {

crates/fff-core/tests/bigram_overlay_coherence_test.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1435,7 +1435,8 @@ fn make_picker(base: &Path) -> (SharedPicker, SharedFrecency) {
14351435
shared_frecency.clone(),
14361436
FilePickerOptions {
14371437
base_path: base.to_string_lossy().to_string(),
1438-
warmup_mmap_cache: true,
1438+
enable_mmap_cache: true,
1439+
enable_content_indexing: true,
14391440
mode: FFFMode::Neovim,
14401441
watch: false, // we drive events manually
14411442
..Default::default()

0 commit comments

Comments
 (0)