Introduce LocalSnapshot

This allows us to remove the absolute path and scan-related state from the Snapshot. None of this data is relevant or valid on guests.
This commit is contained in:
Nathan Sobo 2022-01-22 15:19:14 -07:00
parent d192b6ebc7
commit 506ce8e032
2 changed files with 81 additions and 64 deletions

View file

@ -1,7 +1,7 @@
[package] [package]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/project.rs" path = "src/project.rs"

View file

@ -30,7 +30,7 @@ use std::{
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
fmt, fmt,
future::Future, future::Future,
ops::Deref, ops::{Deref, DerefMut},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
@ -54,9 +54,9 @@ pub enum Worktree {
} }
pub struct LocalWorktree { pub struct LocalWorktree {
snapshot: Snapshot, snapshot: LocalSnapshot,
config: WorktreeConfig, config: WorktreeConfig,
background_snapshot: Arc<Mutex<Snapshot>>, background_snapshot: Arc<Mutex<LocalSnapshot>>,
last_scan_state_rx: watch::Receiver<ScanState>, last_scan_state_rx: watch::Receiver<ScanState>,
_background_scanner_task: Option<Task<()>>, _background_scanner_task: Option<Task<()>>,
poll_task: Option<Task<()>>, poll_task: Option<Task<()>>,
@ -85,15 +85,34 @@ pub struct RemoteWorktree {
#[derive(Clone)] #[derive(Clone)]
pub struct Snapshot { pub struct Snapshot {
id: WorktreeId, id: WorktreeId,
scan_id: usize,
abs_path: Arc<Path>,
root_name: String, root_name: String,
root_char_bag: CharBag, root_char_bag: CharBag,
ignores: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
entries_by_path: SumTree<Entry>, entries_by_path: SumTree<Entry>,
entries_by_id: SumTree<PathEntry>, entries_by_id: SumTree<PathEntry>,
}
#[derive(Clone)]
pub struct LocalSnapshot {
abs_path: Arc<Path>,
scan_id: usize,
ignores: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
removed_entry_ids: HashMap<u64, usize>, removed_entry_ids: HashMap<u64, usize>,
next_entry_id: Arc<AtomicUsize>, next_entry_id: Arc<AtomicUsize>,
snapshot: Snapshot,
}
impl Deref for LocalSnapshot {
type Target = Snapshot;
fn deref(&self) -> &Self::Target {
&self.snapshot
}
}
impl DerefMut for LocalSnapshot {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.snapshot
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -112,7 +131,7 @@ enum Registration {
struct ShareState { struct ShareState {
project_id: u64, project_id: u64,
snapshots_tx: Sender<Snapshot>, snapshots_tx: Sender<LocalSnapshot>,
_maintain_remote_snapshot: Option<Task<()>>, _maintain_remote_snapshot: Option<Task<()>>,
} }
@ -158,7 +177,7 @@ impl Worktree {
let (tree, scan_states_tx) = LocalWorktree::new(client, path, weak, fs.clone(), cx).await?; let (tree, scan_states_tx) = LocalWorktree::new(client, path, weak, fs.clone(), cx).await?;
tree.update(cx, |tree, cx| { tree.update(cx, |tree, cx| {
let tree = tree.as_local_mut().unwrap(); let tree = tree.as_local_mut().unwrap();
let abs_path = tree.snapshot.abs_path.clone(); let abs_path = tree.abs_path().clone();
let background_snapshot = tree.background_snapshot.clone(); let background_snapshot = tree.background_snapshot.clone();
let background = cx.background().clone(); let background = cx.background().clone();
tree._background_scanner_task = Some(cx.background().spawn(async move { tree._background_scanner_task = Some(cx.background().spawn(async move {
@ -233,15 +252,10 @@ impl Worktree {
cx.add_model(|cx: &mut ModelContext<Worktree>| { cx.add_model(|cx: &mut ModelContext<Worktree>| {
let snapshot = Snapshot { let snapshot = Snapshot {
id: WorktreeId(remote_id as usize), id: WorktreeId(remote_id as usize),
scan_id: 0,
abs_path: Path::new("").into(),
root_name, root_name,
root_char_bag, root_char_bag,
ignores: Default::default(),
entries_by_path, entries_by_path,
entries_by_id, entries_by_id,
removed_entry_ids: Default::default(),
next_entry_id: Default::default(),
}; };
let (updates_tx, mut updates_rx) = postage::mpsc::channel(64); let (updates_tx, mut updates_rx) = postage::mpsc::channel(64);
@ -328,7 +342,7 @@ impl Worktree {
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> Snapshot {
match self { match self {
Worktree::Local(worktree) => worktree.snapshot(), Worktree::Local(worktree) => worktree.snapshot().snapshot,
Worktree::Remote(worktree) => worktree.snapshot(), Worktree::Remote(worktree) => worktree.snapshot(),
} }
} }
@ -469,28 +483,28 @@ impl LocalWorktree {
let (scan_states_tx, scan_states_rx) = smol::channel::unbounded(); let (scan_states_tx, scan_states_rx) = smol::channel::unbounded();
let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning); let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning);
let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| { let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| {
let mut snapshot = Snapshot { let mut snapshot = LocalSnapshot {
id: WorktreeId::from_usize(cx.model_id()),
scan_id: 0,
abs_path, abs_path,
root_name: root_name.clone(), scan_id: 0,
root_char_bag,
ignores: Default::default(), ignores: Default::default(),
entries_by_path: Default::default(),
entries_by_id: Default::default(),
removed_entry_ids: Default::default(), removed_entry_ids: Default::default(),
next_entry_id: Arc::new(next_entry_id), next_entry_id: Arc::new(next_entry_id),
snapshot: Snapshot {
id: WorktreeId::from_usize(cx.model_id()),
root_name: root_name.clone(),
root_char_bag,
entries_by_path: Default::default(),
entries_by_id: Default::default(),
},
}; };
if let Some(metadata) = metadata { if let Some(metadata) = metadata {
snapshot.insert_entry( let entry = Entry::new(
Entry::new( path.into(),
path.into(), &metadata,
&metadata, &snapshot.next_entry_id,
&snapshot.next_entry_id, snapshot.root_char_bag,
snapshot.root_char_bag,
),
fs.as_ref(),
); );
snapshot.insert_entry(entry, fs.as_ref());
} }
let tree = Self { let tree = Self {
@ -547,6 +561,18 @@ impl LocalWorktree {
&self.abs_path &self.abs_path
} }
pub fn contains_abs_path(&self, path: &Path) -> bool {
path.starts_with(&self.abs_path)
}
fn absolutize(&self, path: &Path) -> PathBuf {
if path.file_name().is_some() {
self.abs_path.join(path)
} else {
self.abs_path.to_path_buf()
}
}
pub fn authorized_logins(&self) -> Vec<String> { pub fn authorized_logins(&self) -> Vec<String> {
self.config.collaborators.clone() self.config.collaborators.clone()
} }
@ -628,7 +654,7 @@ impl LocalWorktree {
} }
} }
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> LocalSnapshot {
self.snapshot.clone() self.snapshot.clone()
} }
@ -754,7 +780,8 @@ impl LocalWorktree {
let snapshot = self.snapshot(); let snapshot = self.snapshot();
let rpc = self.client.clone(); let rpc = self.client.clone();
let worktree_id = cx.model_id() as u64; let worktree_id = cx.model_id() as u64;
let (snapshots_to_send_tx, snapshots_to_send_rx) = smol::channel::unbounded::<Snapshot>(); let (snapshots_to_send_tx, snapshots_to_send_rx) =
smol::channel::unbounded::<LocalSnapshot>();
let maintain_remote_snapshot = cx.background().spawn({ let maintain_remote_snapshot = cx.background().spawn({
let rpc = rpc.clone(); let rpc = rpc.clone();
let snapshot = snapshot.clone(); let snapshot = snapshot.clone();
@ -974,9 +1001,6 @@ impl Snapshot {
} }
pub(crate) fn apply_update(&mut self, update: proto::UpdateWorktree) -> Result<()> { pub(crate) fn apply_update(&mut self, update: proto::UpdateWorktree) -> Result<()> {
self.scan_id += 1;
let scan_id = self.scan_id;
let mut entries_by_path_edits = Vec::new(); let mut entries_by_path_edits = Vec::new();
let mut entries_by_id_edits = Vec::new(); let mut entries_by_id_edits = Vec::new();
for entry_id in update.removed_entries { for entry_id in update.removed_entries {
@ -997,7 +1021,7 @@ impl Snapshot {
id: entry.id, id: entry.id,
path: entry.path.clone(), path: entry.path.clone(),
is_ignored: entry.is_ignored, is_ignored: entry.is_ignored,
scan_id, scan_id: 0,
})); }));
entries_by_path_edits.push(Edit::Insert(entry)); entries_by_path_edits.push(Edit::Insert(entry));
} }
@ -1084,18 +1108,6 @@ impl Snapshot {
} }
} }
pub fn contains_abs_path(&self, path: &Path) -> bool {
path.starts_with(&self.abs_path)
}
fn absolutize(&self, path: &Path) -> PathBuf {
if path.file_name().is_some() {
self.abs_path.join(path)
} else {
self.abs_path.to_path_buf()
}
}
pub fn root_entry(&self) -> Option<&Entry> { pub fn root_entry(&self) -> Option<&Entry> {
self.entry_for_path("") self.entry_for_path("")
} }
@ -1125,7 +1137,9 @@ impl Snapshot {
pub fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> { pub fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> {
self.entry_for_path(path.as_ref()).map(|e| e.inode) self.entry_for_path(path.as_ref()).map(|e| e.inode)
} }
}
impl LocalSnapshot {
fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry { fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry {
if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) { if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) {
let abs_path = self.abs_path.join(&entry.path); let abs_path = self.abs_path.join(&entry.path);
@ -1147,12 +1161,13 @@ impl Snapshot {
self.reuse_entry_id(&mut entry); self.reuse_entry_id(&mut entry);
self.entries_by_path.insert_or_replace(entry.clone(), &()); self.entries_by_path.insert_or_replace(entry.clone(), &());
let scan_id = self.scan_id;
self.entries_by_id.insert_or_replace( self.entries_by_id.insert_or_replace(
PathEntry { PathEntry {
id: entry.id, id: entry.id,
path: entry.path.clone(), path: entry.path.clone(),
is_ignored: entry.is_ignored, is_ignored: entry.is_ignored,
scan_id: self.scan_id, scan_id,
}, },
&(), &(),
); );
@ -1308,7 +1323,7 @@ impl Deref for Worktree {
} }
impl Deref for LocalWorktree { impl Deref for LocalWorktree {
type Target = Snapshot; type Target = LocalSnapshot;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.snapshot &self.snapshot
@ -1679,14 +1694,14 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
struct BackgroundScanner { struct BackgroundScanner {
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
snapshot: Arc<Mutex<Snapshot>>, snapshot: Arc<Mutex<LocalSnapshot>>,
notify: Sender<ScanState>, notify: Sender<ScanState>,
executor: Arc<executor::Background>, executor: Arc<executor::Background>,
} }
impl BackgroundScanner { impl BackgroundScanner {
fn new( fn new(
snapshot: Arc<Mutex<Snapshot>>, snapshot: Arc<Mutex<LocalSnapshot>>,
notify: Sender<ScanState>, notify: Sender<ScanState>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
executor: Arc<executor::Background>, executor: Arc<executor::Background>,
@ -1703,7 +1718,7 @@ impl BackgroundScanner {
self.snapshot.lock().abs_path.clone() self.snapshot.lock().abs_path.clone()
} }
fn snapshot(&self) -> Snapshot { fn snapshot(&self) -> LocalSnapshot {
self.snapshot.lock().clone() self.snapshot.lock().clone()
} }
@ -2046,7 +2061,7 @@ impl BackgroundScanner {
.await; .await;
} }
async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &Snapshot) { async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) {
let mut ignore_stack = job.ignore_stack; let mut ignore_stack = job.ignore_stack;
if let Some((ignore, _)) = snapshot.ignores.get(&job.path) { if let Some((ignore, _)) = snapshot.ignores.get(&job.path) {
ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone()); ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone());
@ -2090,7 +2105,7 @@ impl BackgroundScanner {
async fn refresh_entry( async fn refresh_entry(
fs: &dyn Fs, fs: &dyn Fs,
snapshot: &Mutex<Snapshot>, snapshot: &Mutex<LocalSnapshot>,
path: Arc<Path>, path: Arc<Path>,
abs_path: &Path, abs_path: &Path,
) -> Result<Entry> { ) -> Result<Entry> {
@ -2158,7 +2173,7 @@ impl WorktreeHandle for ModelHandle<Worktree> {
use smol::future::FutureExt; use smol::future::FutureExt;
let filename = "fs-event-sentinel"; let filename = "fs-event-sentinel";
let root_path = cx.read(|cx| self.read(cx).abs_path.clone()); let root_path = cx.read(|cx| self.read(cx).as_local().unwrap().abs_path().clone());
let tree = self.clone(); let tree = self.clone();
async move { async move {
std::fs::write(root_path.join(filename), "").unwrap(); std::fs::write(root_path.join(filename), "").unwrap();
@ -2510,17 +2525,19 @@ mod tests {
let (notify_tx, _notify_rx) = smol::channel::unbounded(); let (notify_tx, _notify_rx) = smol::channel::unbounded();
let fs = Arc::new(RealFs); let fs = Arc::new(RealFs);
let next_entry_id = Arc::new(AtomicUsize::new(0)); let next_entry_id = Arc::new(AtomicUsize::new(0));
let mut initial_snapshot = Snapshot { let mut initial_snapshot = LocalSnapshot {
id: WorktreeId::from_usize(0),
scan_id: 0,
abs_path: root_dir.path().into(), abs_path: root_dir.path().into(),
entries_by_path: Default::default(), scan_id: 0,
entries_by_id: Default::default(),
removed_entry_ids: Default::default(), removed_entry_ids: Default::default(),
ignores: Default::default(), ignores: Default::default(),
root_name: Default::default(),
root_char_bag: Default::default(),
next_entry_id: next_entry_id.clone(), next_entry_id: next_entry_id.clone(),
snapshot: Snapshot {
id: WorktreeId::from_usize(0),
entries_by_path: Default::default(),
entries_by_id: Default::default(),
root_name: Default::default(),
root_char_bag: Default::default(),
},
}; };
initial_snapshot.insert_entry( initial_snapshot.insert_entry(
Entry::new( Entry::new(
@ -2756,7 +2773,7 @@ mod tests {
.collect() .collect()
} }
impl Snapshot { impl LocalSnapshot {
fn check_invariants(&self) { fn check_invariants(&self) {
let mut files = self.files(true, 0); let mut files = self.files(true, 0);
let mut visible_files = self.files(false, 0); let mut visible_files = self.files(false, 0);