Cancel outstanding fuzzy-matching calls before starting a new one

Max Brunsfeld and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/elements/uniform_list.rs |  2 +-
zed/src/file_finder.rs            | 15 ++++++++++++++-
zed/src/worktree.rs               |  1 +
zed/src/worktree/fuzzy.rs         | 11 +++++++++++
4 files changed, 27 insertions(+), 2 deletions(-)

Detailed changes

zed/src/file_finder.rs 🔗

@@ -14,7 +14,14 @@ use gpui::{
     AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext,
     ViewHandle, WeakViewHandle,
 };
-use std::{cmp, path::Path, sync::Arc};
+use std::{
+    cmp,
+    path::Path,
+    sync::{
+        atomic::{self, AtomicBool},
+        Arc,
+    },
+};
 
 pub struct FileFinder {
     handle: WeakViewHandle<Self>,
@@ -26,6 +33,7 @@ pub struct FileFinder {
     matches: Vec<PathMatch>,
     include_root_name: bool,
     selected: usize,
+    cancel_flag: Arc<AtomicBool>,
     list_state: UniformListState,
 }
 
@@ -287,6 +295,7 @@ impl FileFinder {
             matches: Vec::new(),
             include_root_name: false,
             selected: 0,
+            cancel_flag: Arc::new(AtomicBool::new(false)),
             list_state: UniformListState::new(),
         }
     }
@@ -358,6 +367,9 @@ impl FileFinder {
             .collect::<Vec<_>>();
         let search_id = util::post_inc(&mut self.search_count);
         let pool = ctx.as_ref().thread_pool().clone();
+        self.cancel_flag.store(true, atomic::Ordering::Relaxed);
+        self.cancel_flag = Arc::new(AtomicBool::new(false));
+        let cancel_flag = self.cancel_flag.clone();
         let task = ctx.background_executor().spawn(async move {
             let include_root_name = snapshots.len() > 1;
             let matches = match_paths(
@@ -367,6 +379,7 @@ impl FileFinder {
                 false,
                 false,
                 100,
+                cancel_flag,
                 pool,
             );
             (search_id, include_root_name, matches)

zed/src/worktree.rs 🔗

@@ -1277,6 +1277,7 @@ mod tests {
                     false,
                     false,
                     10,
+                    Default::default(),
                     ctx.thread_pool().clone(),
                 )
                 .into_iter()

zed/src/worktree/fuzzy.rs 🔗

@@ -4,6 +4,7 @@ use std::{
     cmp::{max, min, Ordering, Reverse},
     collections::BinaryHeap,
     path::Path,
+    sync::atomic::{self, AtomicBool},
     sync::Arc,
 };
 
@@ -52,6 +53,7 @@ pub fn match_paths<'a, T>(
     include_ignored: bool,
     smart_case: bool,
     max_results: usize,
+    cancel_flag: Arc<AtomicBool>,
     pool: scoped_pool::Pool,
 ) -> Vec<PathMatch>
 where
@@ -77,6 +79,7 @@ where
     pool.scoped(|scope| {
         for (segment_idx, results) in segment_results.iter_mut().enumerate() {
             let trees = snapshots.clone();
+            let cancel_flag = &cancel_flag;
             scope.execute(move || {
                 let segment_start = segment_idx * segment_size;
                 let segment_end = segment_start + segment_size;
@@ -130,6 +133,7 @@ where
                             &mut last_positions,
                             &mut score_matrix,
                             &mut best_position_matrix,
+                            &cancel_flag,
                         );
                     }
                     if tree_end >= segment_end {
@@ -166,6 +170,7 @@ fn match_single_tree_paths<'a>(
     last_positions: &mut Vec<usize>,
     score_matrix: &mut Vec<Option<f64>>,
     best_position_matrix: &mut Vec<usize>,
+    cancel_flag: &AtomicBool,
 ) {
     let mut path_chars = Vec::new();
     let mut lowercase_path_chars = Vec::new();
@@ -187,6 +192,10 @@ fn match_single_tree_paths<'a>(
             continue;
         }
 
+        if cancel_flag.load(atomic::Ordering::Relaxed) {
+            break;
+        }
+
         path_chars.clear();
         lowercase_path_chars.clear();
         for c in path_entry.path.to_string_lossy().chars() {
@@ -550,6 +559,7 @@ mod tests {
         match_positions.resize(query.len(), 0);
         last_positions.resize(query.len(), 0);
 
+        let cancel_flag = AtomicBool::new(false);
         let mut results = BinaryHeap::new();
         match_single_tree_paths(
             &Snapshot {
@@ -573,6 +583,7 @@ mod tests {
             &mut last_positions,
             &mut Vec::new(),
             &mut Vec::new(),
+            &cancel_flag,
         );
 
         results