From 5648c67d5437558aad8667fcfc5c70bd276c6446 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 15 Apr 2021 20:29:45 -0600 Subject: [PATCH] Perform path matching on Worktree snapshots We're going to need something that can be moved to a background thread. Worktree used to be easy to clone, but that's no longer really true. Instead we can take a snapshot. --- zed/src/worktree.rs | 40 ++++++++++++++++++++++++++++++++++----- zed/src/worktree/fuzzy.rs | 20 ++++++++++---------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index db45fb79c36e8eb1a10cac8d678557bb5018cf65..6e5191b7602fe496018dd389ada1921ab8d5bc59 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -2,7 +2,7 @@ mod char_bag; mod fuzzy; use crate::{ - editor::Snapshot, + editor::Snapshot as BufferSnapshot, sum_tree::{self, Edit, SumTree}, }; use anyhow::{anyhow, Result}; @@ -50,6 +50,12 @@ pub struct Worktree { poll_scheduled: bool, } +pub struct Snapshot { + id: usize, + root_inode: Option, + entries: SumTree, +} + #[derive(Clone)] pub struct FileHandle { worktree: ModelHandle, @@ -57,7 +63,7 @@ pub struct FileHandle { } impl Worktree { - fn new(path: impl Into>, ctx: &mut ModelContext) -> Self { + pub fn new(path: impl Into>, ctx: &mut ModelContext) -> Self { let path = path.into(); let scan_state = smol::channel::unbounded(); let scanner = BackgroundScanner::new(path.clone(), scan_state.0); @@ -79,6 +85,14 @@ impl Worktree { tree } + pub fn snapshot(&self) -> Snapshot { + Snapshot { + id: self.id, + root_inode: self.root_inode(), + entries: self.entries.clone(), + } + } + fn observe_scan_state(&mut self, scan_state: ScanState, ctx: &mut ModelContext) { self.scan_state = scan_state; self.poll_entries(ctx); @@ -195,7 +209,12 @@ impl Worktree { }) } - pub fn save<'a>(&self, ino: u64, content: Snapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>( + &self, + ino: u64, + content: BufferSnapshot, + ctx: &AppContext, + ) -> Task> { let path = self.abs_path_for_inode(ino); eprintln!("save to path: {:?}", path); ctx.background_executor().spawn(async move { @@ -250,6 +269,16 @@ impl fmt::Debug for Worktree { } } +impl Snapshot { + pub fn file_count(&self) -> usize { + self.entries.summary().file_count + } + + pub fn root_entry(&self) -> Option<&Entry> { + self.root_inode.and_then(|inode| self.entries.get(&inode)) + } +} + impl FileHandle { pub fn path(&self, ctx: &AppContext) -> PathBuf { self.worktree @@ -262,7 +291,7 @@ impl FileHandle { self.worktree.read(ctx).load_file(self.inode, ctx) } - pub fn save<'a>(&self, content: Snapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); worktree.save(self.inode, content, ctx) } @@ -397,6 +426,7 @@ impl BackgroundScanner { Duration::from_millis(100), |events| { eprintln!("events: {:?}", events); + true }, ); @@ -640,7 +670,7 @@ mod tests { app.read(|ctx| { let tree = tree.read(ctx); let results = match_paths( - Some(tree).into_iter(), + Some(tree.snapshot()).iter(), "bna", false, false, diff --git a/zed/src/worktree/fuzzy.rs b/zed/src/worktree/fuzzy.rs index 22f48a15c993a709efab152947ebc73593ac90cb..1b03fbb76951b14b00b7c9552f1cd0ad6a4be83f 100644 --- a/zed/src/worktree/fuzzy.rs +++ b/zed/src/worktree/fuzzy.rs @@ -2,7 +2,7 @@ use gpui::scoped_pool; use crate::sum_tree::SeekBias; -use super::{char_bag::CharBag, Entry, FileCount, Worktree}; +use super::{char_bag::CharBag, Entry, FileCount, Snapshot, Worktree}; use std::{ cmp::{max, min, Ordering, Reverse}, @@ -71,7 +71,7 @@ impl Ord for PathMatch { } pub fn match_paths<'a, T>( - trees: T, + snapshots: T, query: &str, include_root_name: bool, include_ignored: bool, @@ -80,7 +80,7 @@ pub fn match_paths<'a, T>( pool: scoped_pool::Pool, ) -> Vec where - T: Clone + Send + Iterator, + T: Clone + Send + Iterator, { let lowercase_query = query.to_lowercase().chars().collect::>(); let query = query.chars().collect::>(); @@ -89,13 +89,13 @@ where let query_chars = CharBag::from(&lowercase_query[..]); let cpus = num_cpus::get(); - let path_count: usize = trees.clone().map(Worktree::file_count).sum(); + let path_count: usize = snapshots.clone().map(Snapshot::file_count).sum(); let segment_size = (path_count + cpus - 1) / cpus; let mut segment_results = (0..cpus).map(|_| BinaryHeap::new()).collect::>(); pool.scoped(|scope| { for (segment_idx, results) in segment_results.iter_mut().enumerate() { - let trees = trees.clone(); + let trees = snapshots.clone(); scope.execute(move || { let segment_start = segment_idx * segment_size; let segment_end = segment_start + segment_size; @@ -109,12 +109,12 @@ where let mut best_position_matrix = Vec::new(); let mut tree_start = 0; - for tree in trees { - let tree_end = tree_start + tree.file_count(); + for snapshot in trees { + let tree_end = tree_start + snapshot.file_count(); if tree_start < segment_end && segment_start < tree_end { let start = max(tree_start, segment_start) - tree_start; let end = min(tree_end, segment_end) - tree_start; - let mut cursor = tree.entries.cursor::<_, ()>(); + let mut cursor = snapshot.entries.cursor::<_, ()>(); cursor.seek(&FileCount(start), SeekBias::Right); let path_entries = cursor .filter_map(|e| { @@ -128,7 +128,7 @@ where let skipped_prefix_len = if include_root_name { 0 - } else if let Some(Entry::Dir { name, .. }) = tree.root_entry() { + } else if let Some(Entry::Dir { name, .. }) = snapshot.root_entry() { let name = name.to_string_lossy(); if name == "/" { 1 @@ -140,7 +140,7 @@ where }; match_single_tree_paths( - tree.id, + snapshot.id, skipped_prefix_len, path_entries, query,