1use super::{
2 fs::{self, Fs},
3 ignore::IgnoreStack,
4 DiagnosticSummary,
5};
6use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
7use anyhow::{anyhow, Context, Result};
8use client::{proto, Client, TypedEnvelope};
9use clock::ReplicaId;
10use collections::HashMap;
11use futures::{
12 channel::mpsc::{self, UnboundedSender},
13 Stream, StreamExt,
14};
15use fuzzy::CharBag;
16use gpui::{
17 executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
18 Task,
19};
20use language::{Buffer, DiagnosticEntry, Operation, PointUtf16, Rope};
21use lazy_static::lazy_static;
22use parking_lot::Mutex;
23use postage::{
24 oneshot,
25 prelude::{Sink as _, Stream as _},
26 watch,
27};
28use serde::Deserialize;
29use smol::channel::{self, Sender};
30use std::{
31 any::Any,
32 cmp::{self, Ordering},
33 convert::{TryFrom, TryInto},
34 ffi::{OsStr, OsString},
35 fmt,
36 future::Future,
37 ops::{Deref, DerefMut},
38 path::{Path, PathBuf},
39 sync::{
40 atomic::{AtomicUsize, Ordering::SeqCst},
41 Arc,
42 },
43 time::{Duration, SystemTime},
44};
45use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap};
46use util::{ResultExt, TryFutureExt};
47
48lazy_static! {
49 static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
50}
51
52#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
53pub struct WorktreeId(usize);
54
55pub enum Worktree {
56 Local(LocalWorktree),
57 Remote(RemoteWorktree),
58}
59
60pub struct LocalWorktree {
61 snapshot: LocalSnapshot,
62 config: WorktreeConfig,
63 background_snapshot: Arc<Mutex<LocalSnapshot>>,
64 last_scan_state_rx: watch::Receiver<ScanState>,
65 _background_scanner_task: Option<Task<()>>,
66 poll_task: Option<Task<()>>,
67 registration: Registration,
68 share: Option<ShareState>,
69 diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<PointUtf16>>>,
70 diagnostic_summaries: TreeMap<PathKey, DiagnosticSummary>,
71 queued_operations: Vec<(u64, Operation)>,
72 client: Arc<Client>,
73 fs: Arc<dyn Fs>,
74 weak: bool,
75}
76
77pub struct RemoteWorktree {
78 pub(crate) snapshot: Snapshot,
79 project_id: u64,
80 snapshot_rx: watch::Receiver<Snapshot>,
81 client: Arc<Client>,
82 updates_tx: UnboundedSender<proto::UpdateWorktree>,
83 replica_id: ReplicaId,
84 queued_operations: Vec<(u64, Operation)>,
85 diagnostic_summaries: TreeMap<PathKey, DiagnosticSummary>,
86 weak: bool,
87}
88
89#[derive(Clone)]
90pub struct Snapshot {
91 id: WorktreeId,
92 root_name: String,
93 root_char_bag: CharBag,
94 entries_by_path: SumTree<Entry>,
95 entries_by_id: SumTree<PathEntry>,
96}
97
98#[derive(Clone)]
99pub struct LocalSnapshot {
100 abs_path: Arc<Path>,
101 scan_id: usize,
102 ignores: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
103 removed_entry_ids: HashMap<u64, usize>,
104 next_entry_id: Arc<AtomicUsize>,
105 snapshot: Snapshot,
106}
107
108impl Deref for LocalSnapshot {
109 type Target = Snapshot;
110
111 fn deref(&self) -> &Self::Target {
112 &self.snapshot
113 }
114}
115
116impl DerefMut for LocalSnapshot {
117 fn deref_mut(&mut self) -> &mut Self::Target {
118 &mut self.snapshot
119 }
120}
121
122#[derive(Clone, Debug)]
123enum ScanState {
124 Idle,
125 Scanning,
126 Err(Arc<anyhow::Error>),
127}
128
129#[derive(Debug, Eq, PartialEq)]
130enum Registration {
131 None,
132 Pending,
133 Done { project_id: u64 },
134}
135
136struct ShareState {
137 project_id: u64,
138 snapshots_tx: Sender<LocalSnapshot>,
139 _maintain_remote_snapshot: Option<Task<Option<()>>>,
140}
141
142#[derive(Default, Deserialize)]
143struct WorktreeConfig {
144 collaborators: Vec<String>,
145}
146
147pub enum Event {
148 UpdatedEntries,
149}
150
151impl Entity for Worktree {
152 type Event = Event;
153
154 fn release(&mut self, _: &mut MutableAppContext) {
155 if let Some(worktree) = self.as_local_mut() {
156 if let Registration::Done { project_id } = worktree.registration {
157 let client = worktree.client.clone();
158 let unregister_message = proto::UnregisterWorktree {
159 project_id,
160 worktree_id: worktree.id().to_proto(),
161 };
162 client.send(unregister_message).log_err();
163 }
164 }
165 }
166}
167
168impl Worktree {
169 pub async fn local(
170 client: Arc<Client>,
171 path: impl Into<Arc<Path>>,
172 weak: bool,
173 fs: Arc<dyn Fs>,
174 cx: &mut AsyncAppContext,
175 ) -> Result<ModelHandle<Self>> {
176 let (tree, scan_states_tx) = LocalWorktree::new(client, path, weak, fs.clone(), cx).await?;
177 tree.update(cx, |tree, cx| {
178 let tree = tree.as_local_mut().unwrap();
179 let abs_path = tree.abs_path().clone();
180 let background_snapshot = tree.background_snapshot.clone();
181 let background = cx.background().clone();
182 tree._background_scanner_task = Some(cx.background().spawn(async move {
183 let events = fs.watch(&abs_path, Duration::from_millis(100)).await;
184 let scanner =
185 BackgroundScanner::new(background_snapshot, scan_states_tx, fs, background);
186 scanner.run(events).await;
187 }));
188 });
189 Ok(tree)
190 }
191
192 pub fn remote(
193 project_remote_id: u64,
194 replica_id: ReplicaId,
195 worktree: proto::Worktree,
196 client: Arc<Client>,
197 cx: &mut MutableAppContext,
198 ) -> (ModelHandle<Self>, Task<()>) {
199 let remote_id = worktree.id;
200 let root_char_bag: CharBag = worktree
201 .root_name
202 .chars()
203 .map(|c| c.to_ascii_lowercase())
204 .collect();
205 let root_name = worktree.root_name.clone();
206 let weak = worktree.weak;
207 let snapshot = Snapshot {
208 id: WorktreeId(remote_id as usize),
209 root_name,
210 root_char_bag,
211 entries_by_path: Default::default(),
212 entries_by_id: Default::default(),
213 };
214
215 let (updates_tx, mut updates_rx) = mpsc::unbounded();
216 let (mut snapshot_tx, snapshot_rx) = watch::channel_with(snapshot.clone());
217 let worktree_handle = cx.add_model(|_: &mut ModelContext<Worktree>| {
218 Worktree::Remote(RemoteWorktree {
219 project_id: project_remote_id,
220 replica_id,
221 snapshot: snapshot.clone(),
222 snapshot_rx: snapshot_rx.clone(),
223 updates_tx,
224 client: client.clone(),
225 queued_operations: Default::default(),
226 diagnostic_summaries: TreeMap::from_ordered_entries(
227 worktree.diagnostic_summaries.into_iter().map(|summary| {
228 (
229 PathKey(PathBuf::from(summary.path).into()),
230 DiagnosticSummary {
231 error_count: summary.error_count as usize,
232 warning_count: summary.warning_count as usize,
233 info_count: summary.info_count as usize,
234 hint_count: summary.hint_count as usize,
235 },
236 )
237 }),
238 ),
239 weak,
240 })
241 });
242
243 let deserialize_task = cx.spawn({
244 let worktree_handle = worktree_handle.clone();
245 |cx| async move {
246 let (entries_by_path, entries_by_id) = cx
247 .background()
248 .spawn(async move {
249 let mut entries_by_path_edits = Vec::new();
250 let mut entries_by_id_edits = Vec::new();
251 for entry in worktree.entries {
252 match Entry::try_from((&root_char_bag, entry)) {
253 Ok(entry) => {
254 entries_by_id_edits.push(Edit::Insert(PathEntry {
255 id: entry.id,
256 path: entry.path.clone(),
257 is_ignored: entry.is_ignored,
258 scan_id: 0,
259 }));
260 entries_by_path_edits.push(Edit::Insert(entry));
261 }
262 Err(err) => log::warn!("error for remote worktree entry {:?}", err),
263 }
264 }
265
266 let mut entries_by_path = SumTree::new();
267 let mut entries_by_id = SumTree::new();
268 entries_by_path.edit(entries_by_path_edits, &());
269 entries_by_id.edit(entries_by_id_edits, &());
270
271 (entries_by_path, entries_by_id)
272 })
273 .await;
274
275 {
276 let mut snapshot = snapshot_tx.borrow_mut();
277 snapshot.entries_by_path = entries_by_path;
278 snapshot.entries_by_id = entries_by_id;
279 }
280
281 cx.background()
282 .spawn(async move {
283 while let Some(update) = updates_rx.next().await {
284 let mut snapshot = snapshot_tx.borrow().clone();
285 if let Err(error) = snapshot.apply_remote_update(update) {
286 log::error!("error applying worktree update: {}", error);
287 }
288 *snapshot_tx.borrow_mut() = snapshot;
289 }
290 })
291 .detach();
292
293 {
294 let mut snapshot_rx = snapshot_rx.clone();
295 let this = worktree_handle.downgrade();
296 cx.spawn(|mut cx| async move {
297 while let Some(_) = snapshot_rx.recv().await {
298 if let Some(this) = this.upgrade(&cx) {
299 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
300 } else {
301 break;
302 }
303 }
304 })
305 .detach();
306 }
307 }
308 });
309 (worktree_handle, deserialize_task)
310 }
311
312 pub fn as_local(&self) -> Option<&LocalWorktree> {
313 if let Worktree::Local(worktree) = self {
314 Some(worktree)
315 } else {
316 None
317 }
318 }
319
320 pub fn as_remote(&self) -> Option<&RemoteWorktree> {
321 if let Worktree::Remote(worktree) = self {
322 Some(worktree)
323 } else {
324 None
325 }
326 }
327
328 pub fn as_local_mut(&mut self) -> Option<&mut LocalWorktree> {
329 if let Worktree::Local(worktree) = self {
330 Some(worktree)
331 } else {
332 None
333 }
334 }
335
336 pub fn as_remote_mut(&mut self) -> Option<&mut RemoteWorktree> {
337 if let Worktree::Remote(worktree) = self {
338 Some(worktree)
339 } else {
340 None
341 }
342 }
343
344 pub fn is_local(&self) -> bool {
345 matches!(self, Worktree::Local(_))
346 }
347
348 pub fn snapshot(&self) -> Snapshot {
349 match self {
350 Worktree::Local(worktree) => worktree.snapshot().snapshot,
351 Worktree::Remote(worktree) => worktree.snapshot(),
352 }
353 }
354
355 pub fn is_weak(&self) -> bool {
356 match self {
357 Worktree::Local(worktree) => worktree.weak,
358 Worktree::Remote(worktree) => worktree.weak,
359 }
360 }
361
362 pub fn replica_id(&self) -> ReplicaId {
363 match self {
364 Worktree::Local(_) => 0,
365 Worktree::Remote(worktree) => worktree.replica_id,
366 }
367 }
368
369 pub fn diagnostic_summaries<'a>(
370 &'a self,
371 ) -> impl Iterator<Item = (Arc<Path>, DiagnosticSummary)> + 'a {
372 match self {
373 Worktree::Local(worktree) => &worktree.diagnostic_summaries,
374 Worktree::Remote(worktree) => &worktree.diagnostic_summaries,
375 }
376 .iter()
377 .map(|(path, summary)| (path.0.clone(), summary.clone()))
378 }
379
380 fn poll_snapshot(&mut self, cx: &mut ModelContext<Self>) {
381 match self {
382 Self::Local(worktree) => {
383 let is_fake_fs = worktree.fs.is_fake();
384 worktree.snapshot = worktree.background_snapshot.lock().clone();
385 if worktree.is_scanning() {
386 if worktree.poll_task.is_none() {
387 worktree.poll_task = Some(cx.spawn_weak(|this, mut cx| async move {
388 if is_fake_fs {
389 cx.background().simulate_random_delay().await;
390 } else {
391 smol::Timer::after(Duration::from_millis(100)).await;
392 }
393 if let Some(this) = this.upgrade(&cx) {
394 this.update(&mut cx, |this, cx| {
395 this.as_local_mut().unwrap().poll_task = None;
396 this.poll_snapshot(cx);
397 });
398 }
399 }));
400 }
401 } else {
402 worktree.poll_task.take();
403 cx.emit(Event::UpdatedEntries);
404 }
405 }
406 Self::Remote(worktree) => {
407 worktree.snapshot = worktree.snapshot_rx.borrow().clone();
408 cx.emit(Event::UpdatedEntries);
409 }
410 };
411
412 cx.notify();
413 }
414
415 fn send_buffer_update(
416 &mut self,
417 buffer_id: u64,
418 operation: Operation,
419 cx: &mut ModelContext<Self>,
420 ) {
421 if let Some((project_id, rpc)) = match self {
422 Worktree::Local(worktree) => worktree
423 .share
424 .as_ref()
425 .map(|share| (share.project_id, worktree.client.clone())),
426 Worktree::Remote(worktree) => Some((worktree.project_id, worktree.client.clone())),
427 } {
428 cx.spawn(|worktree, mut cx| async move {
429 if let Err(error) = rpc
430 .request(proto::UpdateBuffer {
431 project_id,
432 buffer_id,
433 operations: vec![language::proto::serialize_operation(&operation)],
434 })
435 .await
436 {
437 worktree.update(&mut cx, |worktree, _| {
438 log::error!("error sending buffer operation: {}", error);
439 match worktree {
440 Worktree::Local(t) => &mut t.queued_operations,
441 Worktree::Remote(t) => &mut t.queued_operations,
442 }
443 .push((buffer_id, operation));
444 });
445 }
446 })
447 .detach();
448 }
449 }
450}
451
452impl LocalWorktree {
453 async fn new(
454 client: Arc<Client>,
455 path: impl Into<Arc<Path>>,
456 weak: bool,
457 fs: Arc<dyn Fs>,
458 cx: &mut AsyncAppContext,
459 ) -> Result<(ModelHandle<Worktree>, UnboundedSender<ScanState>)> {
460 let abs_path = path.into();
461 let path: Arc<Path> = Arc::from(Path::new(""));
462 let next_entry_id = AtomicUsize::new(0);
463
464 // After determining whether the root entry is a file or a directory, populate the
465 // snapshot's "root name", which will be used for the purpose of fuzzy matching.
466 let root_name = abs_path
467 .file_name()
468 .map_or(String::new(), |f| f.to_string_lossy().to_string());
469 let root_char_bag = root_name.chars().map(|c| c.to_ascii_lowercase()).collect();
470 let metadata = fs
471 .metadata(&abs_path)
472 .await
473 .context("failed to stat worktree path")?;
474
475 let mut config = WorktreeConfig::default();
476 if let Ok(zed_toml) = fs.load(&abs_path.join(".zed.toml")).await {
477 if let Ok(parsed) = toml::from_str(&zed_toml) {
478 config = parsed;
479 }
480 }
481
482 let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded();
483 let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning);
484 let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| {
485 let mut snapshot = LocalSnapshot {
486 abs_path,
487 scan_id: 0,
488 ignores: Default::default(),
489 removed_entry_ids: Default::default(),
490 next_entry_id: Arc::new(next_entry_id),
491 snapshot: Snapshot {
492 id: WorktreeId::from_usize(cx.model_id()),
493 root_name: root_name.clone(),
494 root_char_bag,
495 entries_by_path: Default::default(),
496 entries_by_id: Default::default(),
497 },
498 };
499 if let Some(metadata) = metadata {
500 let entry = Entry::new(
501 path.into(),
502 &metadata,
503 &snapshot.next_entry_id,
504 snapshot.root_char_bag,
505 );
506 snapshot.insert_entry(entry, fs.as_ref());
507 }
508
509 let tree = Self {
510 snapshot: snapshot.clone(),
511 config,
512 background_snapshot: Arc::new(Mutex::new(snapshot)),
513 last_scan_state_rx,
514 _background_scanner_task: None,
515 registration: Registration::None,
516 share: None,
517 poll_task: None,
518 diagnostics: Default::default(),
519 diagnostic_summaries: Default::default(),
520 queued_operations: Default::default(),
521 client,
522 fs,
523 weak,
524 };
525
526 cx.spawn_weak(|this, mut cx| async move {
527 while let Some(scan_state) = scan_states_rx.next().await {
528 if let Some(handle) = this.upgrade(&cx) {
529 let to_send = handle.update(&mut cx, |this, cx| {
530 last_scan_state_tx.blocking_send(scan_state).ok();
531 this.poll_snapshot(cx);
532 let tree = this.as_local_mut().unwrap();
533 if !tree.is_scanning() {
534 if let Some(share) = tree.share.as_ref() {
535 return Some((tree.snapshot(), share.snapshots_tx.clone()));
536 }
537 }
538 None
539 });
540
541 if let Some((snapshot, snapshots_to_send_tx)) = to_send {
542 if let Err(err) = snapshots_to_send_tx.send(snapshot).await {
543 log::error!("error submitting snapshot to send {}", err);
544 }
545 }
546 } else {
547 break;
548 }
549 }
550 })
551 .detach();
552
553 Worktree::Local(tree)
554 });
555
556 Ok((tree, scan_states_tx))
557 }
558
559 pub fn contains_abs_path(&self, path: &Path) -> bool {
560 path.starts_with(&self.abs_path)
561 }
562
563 fn absolutize(&self, path: &Path) -> PathBuf {
564 if path.file_name().is_some() {
565 self.abs_path.join(path)
566 } else {
567 self.abs_path.to_path_buf()
568 }
569 }
570
571 pub fn authorized_logins(&self) -> Vec<String> {
572 self.config.collaborators.clone()
573 }
574
575 pub(crate) fn load_buffer(
576 &mut self,
577 path: &Path,
578 cx: &mut ModelContext<Worktree>,
579 ) -> Task<Result<ModelHandle<Buffer>>> {
580 let path = Arc::from(path);
581 cx.spawn(move |this, mut cx| async move {
582 let (file, contents) = this
583 .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
584 .await?;
585 Ok(cx.add_model(|cx| Buffer::from_file(0, contents, Box::new(file), cx)))
586 })
587 }
588
589 pub fn diagnostics_for_path(&self, path: &Path) -> Option<Vec<DiagnosticEntry<PointUtf16>>> {
590 self.diagnostics.get(path).cloned()
591 }
592
593 pub fn update_diagnostics(
594 &mut self,
595 worktree_path: Arc<Path>,
596 diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
597 _: &mut ModelContext<Worktree>,
598 ) -> Result<()> {
599 let summary = DiagnosticSummary::new(&diagnostics);
600 self.diagnostic_summaries
601 .insert(PathKey(worktree_path.clone()), summary.clone());
602 self.diagnostics.insert(worktree_path.clone(), diagnostics);
603
604 if let Some(share) = self.share.as_ref() {
605 self.client
606 .send(proto::UpdateDiagnosticSummary {
607 project_id: share.project_id,
608 worktree_id: self.id().to_proto(),
609 summary: Some(proto::DiagnosticSummary {
610 path: worktree_path.to_string_lossy().to_string(),
611 error_count: summary.error_count as u32,
612 warning_count: summary.warning_count as u32,
613 info_count: summary.info_count as u32,
614 hint_count: summary.hint_count as u32,
615 }),
616 })
617 .log_err();
618 }
619
620 Ok(())
621 }
622
623 pub fn scan_complete(&self) -> impl Future<Output = ()> {
624 let mut scan_state_rx = self.last_scan_state_rx.clone();
625 async move {
626 let mut scan_state = Some(scan_state_rx.borrow().clone());
627 while let Some(ScanState::Scanning) = scan_state {
628 scan_state = scan_state_rx.recv().await;
629 }
630 }
631 }
632
633 fn is_scanning(&self) -> bool {
634 if let ScanState::Scanning = *self.last_scan_state_rx.borrow() {
635 true
636 } else {
637 false
638 }
639 }
640
641 pub fn snapshot(&self) -> LocalSnapshot {
642 self.snapshot.clone()
643 }
644
645 fn load(&self, path: &Path, cx: &mut ModelContext<Worktree>) -> Task<Result<(File, String)>> {
646 let handle = cx.handle();
647 let path = Arc::from(path);
648 let abs_path = self.absolutize(&path);
649 let background_snapshot = self.background_snapshot.clone();
650 let fs = self.fs.clone();
651 cx.spawn(|this, mut cx| async move {
652 let text = fs.load(&abs_path).await?;
653 // Eagerly populate the snapshot with an updated entry for the loaded file
654 let entry = refresh_entry(fs.as_ref(), &background_snapshot, path, &abs_path).await?;
655 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
656 Ok((
657 File {
658 entry_id: Some(entry.id),
659 worktree: handle,
660 path: entry.path,
661 mtime: entry.mtime,
662 is_local: true,
663 },
664 text,
665 ))
666 })
667 }
668
669 pub fn save_buffer_as(
670 &self,
671 buffer_handle: ModelHandle<Buffer>,
672 path: impl Into<Arc<Path>>,
673 cx: &mut ModelContext<Worktree>,
674 ) -> Task<Result<()>> {
675 let buffer = buffer_handle.read(cx);
676 let text = buffer.as_rope().clone();
677 let version = buffer.version();
678 let save = self.save(path, text, cx);
679 let handle = cx.handle();
680 cx.as_mut().spawn(|mut cx| async move {
681 let entry = save.await?;
682 let file = File {
683 entry_id: Some(entry.id),
684 worktree: handle,
685 path: entry.path,
686 mtime: entry.mtime,
687 is_local: true,
688 };
689
690 buffer_handle.update(&mut cx, |buffer, cx| {
691 buffer.did_save(version, file.mtime, Some(Box::new(file)), cx);
692 });
693
694 Ok(())
695 })
696 }
697
698 fn save(
699 &self,
700 path: impl Into<Arc<Path>>,
701 text: Rope,
702 cx: &mut ModelContext<Worktree>,
703 ) -> Task<Result<Entry>> {
704 let path = path.into();
705 let abs_path = self.absolutize(&path);
706 let background_snapshot = self.background_snapshot.clone();
707 let fs = self.fs.clone();
708 let save = cx.background().spawn(async move {
709 fs.save(&abs_path, &text).await?;
710 refresh_entry(fs.as_ref(), &background_snapshot, path.clone(), &abs_path).await
711 });
712
713 cx.spawn(|this, mut cx| async move {
714 let entry = save.await?;
715 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
716 Ok(entry)
717 })
718 }
719
720 pub fn register(
721 &mut self,
722 project_id: u64,
723 cx: &mut ModelContext<Worktree>,
724 ) -> Task<anyhow::Result<()>> {
725 if self.registration != Registration::None {
726 return Task::ready(Ok(()));
727 }
728
729 self.registration = Registration::Pending;
730 let client = self.client.clone();
731 let register_message = proto::RegisterWorktree {
732 project_id,
733 worktree_id: self.id().to_proto(),
734 root_name: self.root_name().to_string(),
735 authorized_logins: self.authorized_logins(),
736 weak: self.weak,
737 };
738 cx.spawn(|this, mut cx| async move {
739 let response = client.request(register_message).await;
740 this.update(&mut cx, |this, _| {
741 let worktree = this.as_local_mut().unwrap();
742 match response {
743 Ok(_) => {
744 worktree.registration = Registration::Done { project_id };
745 Ok(())
746 }
747 Err(error) => {
748 worktree.registration = Registration::None;
749 Err(error)
750 }
751 }
752 })
753 })
754 }
755
756 pub fn share(
757 &mut self,
758 project_id: u64,
759 cx: &mut ModelContext<Worktree>,
760 ) -> impl Future<Output = Result<()>> {
761 let (mut share_tx, mut share_rx) = oneshot::channel();
762 if self.share.is_some() {
763 let _ = share_tx.try_send(Ok(()));
764 } else {
765 let snapshot = self.snapshot();
766 let rpc = self.client.clone();
767 let worktree_id = cx.model_id() as u64;
768 let (snapshots_to_send_tx, snapshots_to_send_rx) =
769 smol::channel::unbounded::<LocalSnapshot>();
770 let maintain_remote_snapshot = cx.background().spawn({
771 let rpc = rpc.clone();
772 let snapshot = snapshot.clone();
773 let diagnostic_summaries = self.diagnostic_summaries.clone();
774 async move {
775 if let Err(error) = rpc
776 .request(proto::UpdateWorktree {
777 project_id,
778 worktree_id,
779 root_name: snapshot.root_name().to_string(),
780 updated_entries: snapshot
781 .entries_by_path
782 .iter()
783 .filter(|e| !e.is_ignored)
784 .map(Into::into)
785 .collect(),
786 removed_entries: Default::default(),
787 })
788 .await
789 {
790 let _ = share_tx.try_send(Err(error));
791 return Err(anyhow!("failed to send initial update worktree"));
792 } else {
793 let _ = share_tx.try_send(Ok(()));
794 }
795
796 for (path, summary) in diagnostic_summaries.iter() {
797 rpc.send(proto::UpdateDiagnosticSummary {
798 project_id,
799 worktree_id,
800 summary: Some(summary.to_proto(&path.0)),
801 })?;
802 }
803
804 let mut prev_snapshot = snapshot;
805 while let Ok(snapshot) = snapshots_to_send_rx.recv().await {
806 let message =
807 snapshot.build_update(&prev_snapshot, project_id, worktree_id, false);
808 rpc.request(message).await?;
809 prev_snapshot = snapshot;
810 }
811
812 Ok::<_, anyhow::Error>(())
813 }
814 .log_err()
815 });
816 self.share = Some(ShareState {
817 project_id,
818 snapshots_tx: snapshots_to_send_tx,
819 _maintain_remote_snapshot: Some(maintain_remote_snapshot),
820 });
821 }
822
823 async move {
824 share_rx
825 .next()
826 .await
827 .unwrap_or_else(|| Err(anyhow!("share ended")))
828 }
829 }
830
831 pub fn unshare(&mut self) {
832 self.share.take();
833 }
834
835 pub fn is_shared(&self) -> bool {
836 self.share.is_some()
837 }
838}
839
840impl RemoteWorktree {
841 fn snapshot(&self) -> Snapshot {
842 self.snapshot.clone()
843 }
844
845 pub fn update_from_remote(
846 &mut self,
847 envelope: TypedEnvelope<proto::UpdateWorktree>,
848 ) -> Result<()> {
849 self.updates_tx
850 .unbounded_send(envelope.payload)
851 .expect("consumer runs to completion");
852
853 Ok(())
854 }
855
856 pub fn update_diagnostic_summary(
857 &mut self,
858 path: Arc<Path>,
859 summary: &proto::DiagnosticSummary,
860 ) {
861 self.diagnostic_summaries.insert(
862 PathKey(path.clone()),
863 DiagnosticSummary {
864 error_count: summary.error_count as usize,
865 warning_count: summary.warning_count as usize,
866 info_count: summary.info_count as usize,
867 hint_count: summary.hint_count as usize,
868 },
869 );
870 }
871}
872
873impl Snapshot {
874 pub fn id(&self) -> WorktreeId {
875 self.id
876 }
877
878 pub(crate) fn apply_remote_update(&mut self, update: proto::UpdateWorktree) -> Result<()> {
879 let mut entries_by_path_edits = Vec::new();
880 let mut entries_by_id_edits = Vec::new();
881 for entry_id in update.removed_entries {
882 let entry_id = entry_id as usize;
883 let entry = self
884 .entry_for_id(entry_id)
885 .ok_or_else(|| anyhow!("unknown entry"))?;
886 entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
887 entries_by_id_edits.push(Edit::Remove(entry.id));
888 }
889
890 for entry in update.updated_entries {
891 let entry = Entry::try_from((&self.root_char_bag, entry))?;
892 if let Some(PathEntry { path, .. }) = self.entries_by_id.get(&entry.id, &()) {
893 entries_by_path_edits.push(Edit::Remove(PathKey(path.clone())));
894 }
895 entries_by_id_edits.push(Edit::Insert(PathEntry {
896 id: entry.id,
897 path: entry.path.clone(),
898 is_ignored: entry.is_ignored,
899 scan_id: 0,
900 }));
901 entries_by_path_edits.push(Edit::Insert(entry));
902 }
903
904 self.entries_by_path.edit(entries_by_path_edits, &());
905 self.entries_by_id.edit(entries_by_id_edits, &());
906
907 Ok(())
908 }
909
910 pub fn file_count(&self) -> usize {
911 self.entries_by_path.summary().file_count
912 }
913
914 pub fn visible_file_count(&self) -> usize {
915 self.entries_by_path.summary().visible_file_count
916 }
917
918 fn traverse_from_offset(
919 &self,
920 include_dirs: bool,
921 include_ignored: bool,
922 start_offset: usize,
923 ) -> Traversal {
924 let mut cursor = self.entries_by_path.cursor();
925 cursor.seek(
926 &TraversalTarget::Count {
927 count: start_offset,
928 include_dirs,
929 include_ignored,
930 },
931 Bias::Right,
932 &(),
933 );
934 Traversal {
935 cursor,
936 include_dirs,
937 include_ignored,
938 }
939 }
940
941 fn traverse_from_path(
942 &self,
943 include_dirs: bool,
944 include_ignored: bool,
945 path: &Path,
946 ) -> Traversal {
947 let mut cursor = self.entries_by_path.cursor();
948 cursor.seek(&TraversalTarget::Path(path), Bias::Left, &());
949 Traversal {
950 cursor,
951 include_dirs,
952 include_ignored,
953 }
954 }
955
956 pub fn files(&self, include_ignored: bool, start: usize) -> Traversal {
957 self.traverse_from_offset(false, include_ignored, start)
958 }
959
960 pub fn entries(&self, include_ignored: bool) -> Traversal {
961 self.traverse_from_offset(true, include_ignored, 0)
962 }
963
964 pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
965 let empty_path = Path::new("");
966 self.entries_by_path
967 .cursor::<()>()
968 .filter(move |entry| entry.path.as_ref() != empty_path)
969 .map(|entry| &entry.path)
970 }
971
972 fn child_entries<'a>(&'a self, parent_path: &'a Path) -> ChildEntriesIter<'a> {
973 let mut cursor = self.entries_by_path.cursor();
974 cursor.seek(&TraversalTarget::Path(parent_path), Bias::Right, &());
975 let traversal = Traversal {
976 cursor,
977 include_dirs: true,
978 include_ignored: true,
979 };
980 ChildEntriesIter {
981 traversal,
982 parent_path,
983 }
984 }
985
986 pub fn root_entry(&self) -> Option<&Entry> {
987 self.entry_for_path("")
988 }
989
990 pub fn root_name(&self) -> &str {
991 &self.root_name
992 }
993
994 pub fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
995 let path = path.as_ref();
996 self.traverse_from_path(true, true, path)
997 .entry()
998 .and_then(|entry| {
999 if entry.path.as_ref() == path {
1000 Some(entry)
1001 } else {
1002 None
1003 }
1004 })
1005 }
1006
1007 pub fn entry_for_id(&self, id: usize) -> Option<&Entry> {
1008 let entry = self.entries_by_id.get(&id, &())?;
1009 self.entry_for_path(&entry.path)
1010 }
1011
1012 pub fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> {
1013 self.entry_for_path(path.as_ref()).map(|e| e.inode)
1014 }
1015}
1016
1017impl LocalSnapshot {
1018 pub fn abs_path(&self) -> &Arc<Path> {
1019 &self.abs_path
1020 }
1021
1022 #[cfg(test)]
1023 pub(crate) fn to_proto(
1024 &self,
1025 diagnostic_summaries: &TreeMap<PathKey, DiagnosticSummary>,
1026 weak: bool,
1027 ) -> proto::Worktree {
1028 let root_name = self.root_name.clone();
1029 proto::Worktree {
1030 id: self.id.0 as u64,
1031 root_name,
1032 entries: self
1033 .entries_by_path
1034 .iter()
1035 .filter(|e| !e.is_ignored)
1036 .map(Into::into)
1037 .collect(),
1038 diagnostic_summaries: diagnostic_summaries
1039 .iter()
1040 .map(|(path, summary)| summary.to_proto(&path.0))
1041 .collect(),
1042 weak,
1043 }
1044 }
1045
1046 pub(crate) fn build_update(
1047 &self,
1048 other: &Self,
1049 project_id: u64,
1050 worktree_id: u64,
1051 include_ignored: bool,
1052 ) -> proto::UpdateWorktree {
1053 let mut updated_entries = Vec::new();
1054 let mut removed_entries = Vec::new();
1055 let mut self_entries = self
1056 .entries_by_id
1057 .cursor::<()>()
1058 .filter(|e| include_ignored || !e.is_ignored)
1059 .peekable();
1060 let mut other_entries = other
1061 .entries_by_id
1062 .cursor::<()>()
1063 .filter(|e| include_ignored || !e.is_ignored)
1064 .peekable();
1065 loop {
1066 match (self_entries.peek(), other_entries.peek()) {
1067 (Some(self_entry), Some(other_entry)) => {
1068 match Ord::cmp(&self_entry.id, &other_entry.id) {
1069 Ordering::Less => {
1070 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1071 updated_entries.push(entry);
1072 self_entries.next();
1073 }
1074 Ordering::Equal => {
1075 if self_entry.scan_id != other_entry.scan_id {
1076 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1077 updated_entries.push(entry);
1078 }
1079
1080 self_entries.next();
1081 other_entries.next();
1082 }
1083 Ordering::Greater => {
1084 removed_entries.push(other_entry.id as u64);
1085 other_entries.next();
1086 }
1087 }
1088 }
1089 (Some(self_entry), None) => {
1090 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1091 updated_entries.push(entry);
1092 self_entries.next();
1093 }
1094 (None, Some(other_entry)) => {
1095 removed_entries.push(other_entry.id as u64);
1096 other_entries.next();
1097 }
1098 (None, None) => break,
1099 }
1100 }
1101
1102 proto::UpdateWorktree {
1103 project_id,
1104 worktree_id,
1105 root_name: self.root_name().to_string(),
1106 updated_entries,
1107 removed_entries,
1108 }
1109 }
1110
1111 fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry {
1112 if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) {
1113 let abs_path = self.abs_path.join(&entry.path);
1114 match build_gitignore(&abs_path, fs) {
1115 Ok(ignore) => {
1116 let ignore_dir_path = entry.path.parent().unwrap();
1117 self.ignores
1118 .insert(ignore_dir_path.into(), (Arc::new(ignore), self.scan_id));
1119 }
1120 Err(error) => {
1121 log::error!(
1122 "error loading .gitignore file {:?} - {:?}",
1123 &entry.path,
1124 error
1125 );
1126 }
1127 }
1128 }
1129
1130 self.reuse_entry_id(&mut entry);
1131 self.entries_by_path.insert_or_replace(entry.clone(), &());
1132 let scan_id = self.scan_id;
1133 self.entries_by_id.insert_or_replace(
1134 PathEntry {
1135 id: entry.id,
1136 path: entry.path.clone(),
1137 is_ignored: entry.is_ignored,
1138 scan_id,
1139 },
1140 &(),
1141 );
1142 entry
1143 }
1144
1145 fn populate_dir(
1146 &mut self,
1147 parent_path: Arc<Path>,
1148 entries: impl IntoIterator<Item = Entry>,
1149 ignore: Option<Arc<Gitignore>>,
1150 ) {
1151 let mut parent_entry = self
1152 .entries_by_path
1153 .get(&PathKey(parent_path.clone()), &())
1154 .unwrap()
1155 .clone();
1156 if let Some(ignore) = ignore {
1157 self.ignores.insert(parent_path, (ignore, self.scan_id));
1158 }
1159 if matches!(parent_entry.kind, EntryKind::PendingDir) {
1160 parent_entry.kind = EntryKind::Dir;
1161 } else {
1162 unreachable!();
1163 }
1164
1165 let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)];
1166 let mut entries_by_id_edits = Vec::new();
1167
1168 for mut entry in entries {
1169 self.reuse_entry_id(&mut entry);
1170 entries_by_id_edits.push(Edit::Insert(PathEntry {
1171 id: entry.id,
1172 path: entry.path.clone(),
1173 is_ignored: entry.is_ignored,
1174 scan_id: self.scan_id,
1175 }));
1176 entries_by_path_edits.push(Edit::Insert(entry));
1177 }
1178
1179 self.entries_by_path.edit(entries_by_path_edits, &());
1180 self.entries_by_id.edit(entries_by_id_edits, &());
1181 }
1182
1183 fn reuse_entry_id(&mut self, entry: &mut Entry) {
1184 if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) {
1185 entry.id = removed_entry_id;
1186 } else if let Some(existing_entry) = self.entry_for_path(&entry.path) {
1187 entry.id = existing_entry.id;
1188 }
1189 }
1190
1191 fn remove_path(&mut self, path: &Path) {
1192 let mut new_entries;
1193 let removed_entries;
1194 {
1195 let mut cursor = self.entries_by_path.cursor::<TraversalProgress>();
1196 new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
1197 removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
1198 new_entries.push_tree(cursor.suffix(&()), &());
1199 }
1200 self.entries_by_path = new_entries;
1201
1202 let mut entries_by_id_edits = Vec::new();
1203 for entry in removed_entries.cursor::<()>() {
1204 let removed_entry_id = self
1205 .removed_entry_ids
1206 .entry(entry.inode)
1207 .or_insert(entry.id);
1208 *removed_entry_id = cmp::max(*removed_entry_id, entry.id);
1209 entries_by_id_edits.push(Edit::Remove(entry.id));
1210 }
1211 self.entries_by_id.edit(entries_by_id_edits, &());
1212
1213 if path.file_name() == Some(&GITIGNORE) {
1214 if let Some((_, scan_id)) = self.ignores.get_mut(path.parent().unwrap()) {
1215 *scan_id = self.scan_id;
1216 }
1217 }
1218 }
1219
1220 fn ignore_stack_for_path(&self, path: &Path, is_dir: bool) -> Arc<IgnoreStack> {
1221 let mut new_ignores = Vec::new();
1222 for ancestor in path.ancestors().skip(1) {
1223 if let Some((ignore, _)) = self.ignores.get(ancestor) {
1224 new_ignores.push((ancestor, Some(ignore.clone())));
1225 } else {
1226 new_ignores.push((ancestor, None));
1227 }
1228 }
1229
1230 let mut ignore_stack = IgnoreStack::none();
1231 for (parent_path, ignore) in new_ignores.into_iter().rev() {
1232 if ignore_stack.is_path_ignored(&parent_path, true) {
1233 ignore_stack = IgnoreStack::all();
1234 break;
1235 } else if let Some(ignore) = ignore {
1236 ignore_stack = ignore_stack.append(Arc::from(parent_path), ignore);
1237 }
1238 }
1239
1240 if ignore_stack.is_path_ignored(path, is_dir) {
1241 ignore_stack = IgnoreStack::all();
1242 }
1243
1244 ignore_stack
1245 }
1246}
1247
1248fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
1249 let contents = smol::block_on(fs.load(&abs_path))?;
1250 let parent = abs_path.parent().unwrap_or(Path::new("/"));
1251 let mut builder = GitignoreBuilder::new(parent);
1252 for line in contents.lines() {
1253 builder.add_line(Some(abs_path.into()), line)?;
1254 }
1255 Ok(builder.build()?)
1256}
1257
1258impl WorktreeId {
1259 pub fn from_usize(handle_id: usize) -> Self {
1260 Self(handle_id)
1261 }
1262
1263 pub(crate) fn from_proto(id: u64) -> Self {
1264 Self(id as usize)
1265 }
1266
1267 pub fn to_proto(&self) -> u64 {
1268 self.0 as u64
1269 }
1270
1271 pub fn to_usize(&self) -> usize {
1272 self.0
1273 }
1274}
1275
1276impl fmt::Display for WorktreeId {
1277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1278 self.0.fmt(f)
1279 }
1280}
1281
1282impl Deref for Worktree {
1283 type Target = Snapshot;
1284
1285 fn deref(&self) -> &Self::Target {
1286 match self {
1287 Worktree::Local(worktree) => &worktree.snapshot,
1288 Worktree::Remote(worktree) => &worktree.snapshot,
1289 }
1290 }
1291}
1292
1293impl Deref for LocalWorktree {
1294 type Target = LocalSnapshot;
1295
1296 fn deref(&self) -> &Self::Target {
1297 &self.snapshot
1298 }
1299}
1300
1301impl Deref for RemoteWorktree {
1302 type Target = Snapshot;
1303
1304 fn deref(&self) -> &Self::Target {
1305 &self.snapshot
1306 }
1307}
1308
1309impl fmt::Debug for LocalWorktree {
1310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1311 self.snapshot.fmt(f)
1312 }
1313}
1314
1315impl fmt::Debug for Snapshot {
1316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1317 struct EntriesById<'a>(&'a SumTree<PathEntry>);
1318 struct EntriesByPath<'a>(&'a SumTree<Entry>);
1319
1320 impl<'a> fmt::Debug for EntriesByPath<'a> {
1321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1322 f.debug_map()
1323 .entries(self.0.iter().map(|entry| (&entry.path, entry.id)))
1324 .finish()
1325 }
1326 }
1327
1328 impl<'a> fmt::Debug for EntriesById<'a> {
1329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1330 f.debug_list().entries(self.0.iter()).finish()
1331 }
1332 }
1333
1334 f.debug_struct("Snapshot")
1335 .field("id", &self.id)
1336 .field("root_name", &self.root_name)
1337 .field("entries_by_path", &EntriesByPath(&self.entries_by_path))
1338 .field("entries_by_id", &EntriesById(&self.entries_by_id))
1339 .finish()
1340 }
1341}
1342
1343#[derive(Clone, PartialEq)]
1344pub struct File {
1345 pub worktree: ModelHandle<Worktree>,
1346 pub path: Arc<Path>,
1347 pub mtime: SystemTime,
1348 pub(crate) entry_id: Option<usize>,
1349 pub(crate) is_local: bool,
1350}
1351
1352impl language::File for File {
1353 fn as_local(&self) -> Option<&dyn language::LocalFile> {
1354 if self.is_local {
1355 Some(self)
1356 } else {
1357 None
1358 }
1359 }
1360
1361 fn mtime(&self) -> SystemTime {
1362 self.mtime
1363 }
1364
1365 fn path(&self) -> &Arc<Path> {
1366 &self.path
1367 }
1368
1369 fn full_path(&self, cx: &AppContext) -> PathBuf {
1370 let mut full_path = PathBuf::new();
1371 full_path.push(self.worktree.read(cx).root_name());
1372 if self.path.components().next().is_some() {
1373 full_path.push(&self.path);
1374 }
1375 full_path
1376 }
1377
1378 /// Returns the last component of this handle's absolute path. If this handle refers to the root
1379 /// of its worktree, then this method will return the name of the worktree itself.
1380 fn file_name(&self, cx: &AppContext) -> OsString {
1381 self.path
1382 .file_name()
1383 .map(|name| name.into())
1384 .unwrap_or_else(|| OsString::from(&self.worktree.read(cx).root_name))
1385 }
1386
1387 fn is_deleted(&self) -> bool {
1388 self.entry_id.is_none()
1389 }
1390
1391 fn save(
1392 &self,
1393 buffer_id: u64,
1394 text: Rope,
1395 version: clock::Global,
1396 cx: &mut MutableAppContext,
1397 ) -> Task<Result<(clock::Global, SystemTime)>> {
1398 self.worktree.update(cx, |worktree, cx| match worktree {
1399 Worktree::Local(worktree) => {
1400 let rpc = worktree.client.clone();
1401 let project_id = worktree.share.as_ref().map(|share| share.project_id);
1402 let save = worktree.save(self.path.clone(), text, cx);
1403 cx.background().spawn(async move {
1404 let entry = save.await?;
1405 if let Some(project_id) = project_id {
1406 rpc.send(proto::BufferSaved {
1407 project_id,
1408 buffer_id,
1409 version: (&version).into(),
1410 mtime: Some(entry.mtime.into()),
1411 })?;
1412 }
1413 Ok((version, entry.mtime))
1414 })
1415 }
1416 Worktree::Remote(worktree) => {
1417 let rpc = worktree.client.clone();
1418 let project_id = worktree.project_id;
1419 cx.foreground().spawn(async move {
1420 let response = rpc
1421 .request(proto::SaveBuffer {
1422 project_id,
1423 buffer_id,
1424 version: (&version).into(),
1425 })
1426 .await?;
1427 let version = response.version.try_into()?;
1428 let mtime = response
1429 .mtime
1430 .ok_or_else(|| anyhow!("missing mtime"))?
1431 .into();
1432 Ok((version, mtime))
1433 })
1434 }
1435 })
1436 }
1437
1438 fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext) {
1439 self.worktree.update(cx, |worktree, cx| {
1440 worktree.send_buffer_update(buffer_id, operation, cx);
1441 });
1442 }
1443
1444 fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext) {
1445 self.worktree.update(cx, |worktree, _| {
1446 if let Worktree::Remote(worktree) = worktree {
1447 worktree
1448 .client
1449 .send(proto::CloseBuffer {
1450 project_id: worktree.project_id,
1451 buffer_id,
1452 })
1453 .log_err();
1454 }
1455 });
1456 }
1457
1458 fn as_any(&self) -> &dyn Any {
1459 self
1460 }
1461
1462 fn to_proto(&self) -> rpc::proto::File {
1463 rpc::proto::File {
1464 worktree_id: self.worktree.id() as u64,
1465 entry_id: self.entry_id.map(|entry_id| entry_id as u64),
1466 path: self.path.to_string_lossy().into(),
1467 mtime: Some(self.mtime.into()),
1468 }
1469 }
1470}
1471
1472impl language::LocalFile for File {
1473 fn abs_path(&self, cx: &AppContext) -> PathBuf {
1474 self.worktree
1475 .read(cx)
1476 .as_local()
1477 .unwrap()
1478 .abs_path
1479 .join(&self.path)
1480 }
1481
1482 fn load(&self, cx: &AppContext) -> Task<Result<String>> {
1483 let worktree = self.worktree.read(cx).as_local().unwrap();
1484 let abs_path = worktree.absolutize(&self.path);
1485 let fs = worktree.fs.clone();
1486 cx.background()
1487 .spawn(async move { fs.load(&abs_path).await })
1488 }
1489
1490 fn buffer_reloaded(
1491 &self,
1492 buffer_id: u64,
1493 version: &clock::Global,
1494 mtime: SystemTime,
1495 cx: &mut MutableAppContext,
1496 ) {
1497 let worktree = self.worktree.read(cx).as_local().unwrap();
1498 if let Some(project_id) = worktree.share.as_ref().map(|share| share.project_id) {
1499 worktree
1500 .client
1501 .send(proto::BufferReloaded {
1502 project_id,
1503 buffer_id,
1504 version: version.into(),
1505 mtime: Some(mtime.into()),
1506 })
1507 .log_err();
1508 }
1509 }
1510}
1511
1512impl File {
1513 pub fn from_proto(
1514 proto: rpc::proto::File,
1515 worktree: ModelHandle<Worktree>,
1516 cx: &AppContext,
1517 ) -> Result<Self> {
1518 let worktree_id = worktree
1519 .read(cx)
1520 .as_remote()
1521 .ok_or_else(|| anyhow!("not remote"))?
1522 .id();
1523
1524 if worktree_id.to_proto() != proto.worktree_id {
1525 return Err(anyhow!("worktree id does not match file"));
1526 }
1527
1528 Ok(Self {
1529 worktree,
1530 path: Path::new(&proto.path).into(),
1531 mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(),
1532 entry_id: proto.entry_id.map(|entry_id| entry_id as usize),
1533 is_local: false,
1534 })
1535 }
1536
1537 pub fn from_dyn(file: Option<&dyn language::File>) -> Option<&Self> {
1538 file.and_then(|f| f.as_any().downcast_ref())
1539 }
1540
1541 pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
1542 self.worktree.read(cx).id()
1543 }
1544}
1545
1546#[derive(Clone, Debug, PartialEq, Eq)]
1547pub struct Entry {
1548 pub id: usize,
1549 pub kind: EntryKind,
1550 pub path: Arc<Path>,
1551 pub inode: u64,
1552 pub mtime: SystemTime,
1553 pub is_symlink: bool,
1554 pub is_ignored: bool,
1555}
1556
1557#[derive(Clone, Debug, PartialEq, Eq)]
1558pub enum EntryKind {
1559 PendingDir,
1560 Dir,
1561 File(CharBag),
1562}
1563
1564impl Entry {
1565 fn new(
1566 path: Arc<Path>,
1567 metadata: &fs::Metadata,
1568 next_entry_id: &AtomicUsize,
1569 root_char_bag: CharBag,
1570 ) -> Self {
1571 Self {
1572 id: next_entry_id.fetch_add(1, SeqCst),
1573 kind: if metadata.is_dir {
1574 EntryKind::PendingDir
1575 } else {
1576 EntryKind::File(char_bag_for_path(root_char_bag, &path))
1577 },
1578 path,
1579 inode: metadata.inode,
1580 mtime: metadata.mtime,
1581 is_symlink: metadata.is_symlink,
1582 is_ignored: false,
1583 }
1584 }
1585
1586 pub fn is_dir(&self) -> bool {
1587 matches!(self.kind, EntryKind::Dir | EntryKind::PendingDir)
1588 }
1589
1590 pub fn is_file(&self) -> bool {
1591 matches!(self.kind, EntryKind::File(_))
1592 }
1593}
1594
1595impl sum_tree::Item for Entry {
1596 type Summary = EntrySummary;
1597
1598 fn summary(&self) -> Self::Summary {
1599 let visible_count = if self.is_ignored { 0 } else { 1 };
1600 let file_count;
1601 let visible_file_count;
1602 if self.is_file() {
1603 file_count = 1;
1604 visible_file_count = visible_count;
1605 } else {
1606 file_count = 0;
1607 visible_file_count = 0;
1608 }
1609
1610 EntrySummary {
1611 max_path: self.path.clone(),
1612 count: 1,
1613 visible_count,
1614 file_count,
1615 visible_file_count,
1616 }
1617 }
1618}
1619
1620impl sum_tree::KeyedItem for Entry {
1621 type Key = PathKey;
1622
1623 fn key(&self) -> Self::Key {
1624 PathKey(self.path.clone())
1625 }
1626}
1627
1628#[derive(Clone, Debug)]
1629pub struct EntrySummary {
1630 max_path: Arc<Path>,
1631 count: usize,
1632 visible_count: usize,
1633 file_count: usize,
1634 visible_file_count: usize,
1635}
1636
1637impl Default for EntrySummary {
1638 fn default() -> Self {
1639 Self {
1640 max_path: Arc::from(Path::new("")),
1641 count: 0,
1642 visible_count: 0,
1643 file_count: 0,
1644 visible_file_count: 0,
1645 }
1646 }
1647}
1648
1649impl sum_tree::Summary for EntrySummary {
1650 type Context = ();
1651
1652 fn add_summary(&mut self, rhs: &Self, _: &()) {
1653 self.max_path = rhs.max_path.clone();
1654 self.visible_count += rhs.visible_count;
1655 self.file_count += rhs.file_count;
1656 self.visible_file_count += rhs.visible_file_count;
1657 }
1658}
1659
1660#[derive(Clone, Debug)]
1661struct PathEntry {
1662 id: usize,
1663 path: Arc<Path>,
1664 is_ignored: bool,
1665 scan_id: usize,
1666}
1667
1668impl sum_tree::Item for PathEntry {
1669 type Summary = PathEntrySummary;
1670
1671 fn summary(&self) -> Self::Summary {
1672 PathEntrySummary { max_id: self.id }
1673 }
1674}
1675
1676impl sum_tree::KeyedItem for PathEntry {
1677 type Key = usize;
1678
1679 fn key(&self) -> Self::Key {
1680 self.id
1681 }
1682}
1683
1684#[derive(Clone, Debug, Default)]
1685struct PathEntrySummary {
1686 max_id: usize,
1687}
1688
1689impl sum_tree::Summary for PathEntrySummary {
1690 type Context = ();
1691
1692 fn add_summary(&mut self, summary: &Self, _: &Self::Context) {
1693 self.max_id = summary.max_id;
1694 }
1695}
1696
1697impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for usize {
1698 fn add_summary(&mut self, summary: &'a PathEntrySummary, _: &()) {
1699 *self = summary.max_id;
1700 }
1701}
1702
1703#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
1704pub struct PathKey(Arc<Path>);
1705
1706impl Default for PathKey {
1707 fn default() -> Self {
1708 Self(Path::new("").into())
1709 }
1710}
1711
1712impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
1713 fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
1714 self.0 = summary.max_path.clone();
1715 }
1716}
1717
1718struct BackgroundScanner {
1719 fs: Arc<dyn Fs>,
1720 snapshot: Arc<Mutex<LocalSnapshot>>,
1721 notify: UnboundedSender<ScanState>,
1722 executor: Arc<executor::Background>,
1723}
1724
1725impl BackgroundScanner {
1726 fn new(
1727 snapshot: Arc<Mutex<LocalSnapshot>>,
1728 notify: UnboundedSender<ScanState>,
1729 fs: Arc<dyn Fs>,
1730 executor: Arc<executor::Background>,
1731 ) -> Self {
1732 Self {
1733 fs,
1734 snapshot,
1735 notify,
1736 executor,
1737 }
1738 }
1739
1740 fn abs_path(&self) -> Arc<Path> {
1741 self.snapshot.lock().abs_path.clone()
1742 }
1743
1744 fn snapshot(&self) -> LocalSnapshot {
1745 self.snapshot.lock().clone()
1746 }
1747
1748 async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
1749 if self.notify.unbounded_send(ScanState::Scanning).is_err() {
1750 return;
1751 }
1752
1753 if let Err(err) = self.scan_dirs().await {
1754 if self
1755 .notify
1756 .unbounded_send(ScanState::Err(Arc::new(err)))
1757 .is_err()
1758 {
1759 return;
1760 }
1761 }
1762
1763 if self.notify.unbounded_send(ScanState::Idle).is_err() {
1764 return;
1765 }
1766
1767 futures::pin_mut!(events_rx);
1768 while let Some(events) = events_rx.next().await {
1769 if self.notify.unbounded_send(ScanState::Scanning).is_err() {
1770 break;
1771 }
1772
1773 if !self.process_events(events).await {
1774 break;
1775 }
1776
1777 if self.notify.unbounded_send(ScanState::Idle).is_err() {
1778 break;
1779 }
1780 }
1781 }
1782
1783 async fn scan_dirs(&mut self) -> Result<()> {
1784 let root_char_bag;
1785 let next_entry_id;
1786 let is_dir;
1787 {
1788 let snapshot = self.snapshot.lock();
1789 root_char_bag = snapshot.root_char_bag;
1790 next_entry_id = snapshot.next_entry_id.clone();
1791 is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir())
1792 };
1793
1794 if is_dir {
1795 let path: Arc<Path> = Arc::from(Path::new(""));
1796 let abs_path = self.abs_path();
1797 let (tx, rx) = channel::unbounded();
1798 tx.send(ScanJob {
1799 abs_path: abs_path.to_path_buf(),
1800 path,
1801 ignore_stack: IgnoreStack::none(),
1802 scan_queue: tx.clone(),
1803 })
1804 .await
1805 .unwrap();
1806 drop(tx);
1807
1808 self.executor
1809 .scoped(|scope| {
1810 for _ in 0..self.executor.num_cpus() {
1811 scope.spawn(async {
1812 while let Ok(job) = rx.recv().await {
1813 if let Err(err) = self
1814 .scan_dir(root_char_bag, next_entry_id.clone(), &job)
1815 .await
1816 {
1817 log::error!("error scanning {:?}: {}", job.abs_path, err);
1818 }
1819 }
1820 });
1821 }
1822 })
1823 .await;
1824 }
1825
1826 Ok(())
1827 }
1828
1829 async fn scan_dir(
1830 &self,
1831 root_char_bag: CharBag,
1832 next_entry_id: Arc<AtomicUsize>,
1833 job: &ScanJob,
1834 ) -> Result<()> {
1835 let mut new_entries: Vec<Entry> = Vec::new();
1836 let mut new_jobs: Vec<ScanJob> = Vec::new();
1837 let mut ignore_stack = job.ignore_stack.clone();
1838 let mut new_ignore = None;
1839
1840 let mut child_paths = self.fs.read_dir(&job.abs_path).await?;
1841 while let Some(child_abs_path) = child_paths.next().await {
1842 let child_abs_path = match child_abs_path {
1843 Ok(child_abs_path) => child_abs_path,
1844 Err(error) => {
1845 log::error!("error processing entry {:?}", error);
1846 continue;
1847 }
1848 };
1849 let child_name = child_abs_path.file_name().unwrap();
1850 let child_path: Arc<Path> = job.path.join(child_name).into();
1851 let child_metadata = match self.fs.metadata(&child_abs_path).await? {
1852 Some(metadata) => metadata,
1853 None => continue,
1854 };
1855
1856 // If we find a .gitignore, add it to the stack of ignores used to determine which paths are ignored
1857 if child_name == *GITIGNORE {
1858 match build_gitignore(&child_abs_path, self.fs.as_ref()) {
1859 Ok(ignore) => {
1860 let ignore = Arc::new(ignore);
1861 ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone());
1862 new_ignore = Some(ignore);
1863 }
1864 Err(error) => {
1865 log::error!(
1866 "error loading .gitignore file {:?} - {:?}",
1867 child_name,
1868 error
1869 );
1870 }
1871 }
1872
1873 // Update ignore status of any child entries we've already processed to reflect the
1874 // ignore file in the current directory. Because `.gitignore` starts with a `.`,
1875 // there should rarely be too numerous. Update the ignore stack associated with any
1876 // new jobs as well.
1877 let mut new_jobs = new_jobs.iter_mut();
1878 for entry in &mut new_entries {
1879 entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir());
1880 if entry.is_dir() {
1881 new_jobs.next().unwrap().ignore_stack = if entry.is_ignored {
1882 IgnoreStack::all()
1883 } else {
1884 ignore_stack.clone()
1885 };
1886 }
1887 }
1888 }
1889
1890 let mut child_entry = Entry::new(
1891 child_path.clone(),
1892 &child_metadata,
1893 &next_entry_id,
1894 root_char_bag,
1895 );
1896
1897 if child_metadata.is_dir {
1898 let is_ignored = ignore_stack.is_path_ignored(&child_path, true);
1899 child_entry.is_ignored = is_ignored;
1900 new_entries.push(child_entry);
1901 new_jobs.push(ScanJob {
1902 abs_path: child_abs_path,
1903 path: child_path,
1904 ignore_stack: if is_ignored {
1905 IgnoreStack::all()
1906 } else {
1907 ignore_stack.clone()
1908 },
1909 scan_queue: job.scan_queue.clone(),
1910 });
1911 } else {
1912 child_entry.is_ignored = ignore_stack.is_path_ignored(&child_path, false);
1913 new_entries.push(child_entry);
1914 };
1915 }
1916
1917 self.snapshot
1918 .lock()
1919 .populate_dir(job.path.clone(), new_entries, new_ignore);
1920 for new_job in new_jobs {
1921 job.scan_queue.send(new_job).await.unwrap();
1922 }
1923
1924 Ok(())
1925 }
1926
1927 async fn process_events(&mut self, mut events: Vec<fsevent::Event>) -> bool {
1928 let mut snapshot = self.snapshot();
1929 snapshot.scan_id += 1;
1930
1931 let root_abs_path = if let Ok(abs_path) = self.fs.canonicalize(&snapshot.abs_path).await {
1932 abs_path
1933 } else {
1934 return false;
1935 };
1936 let root_char_bag = snapshot.root_char_bag;
1937 let next_entry_id = snapshot.next_entry_id.clone();
1938
1939 events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
1940 events.dedup_by(|a, b| a.path.starts_with(&b.path));
1941
1942 for event in &events {
1943 match event.path.strip_prefix(&root_abs_path) {
1944 Ok(path) => snapshot.remove_path(&path),
1945 Err(_) => {
1946 log::error!(
1947 "unexpected event {:?} for root path {:?}",
1948 event.path,
1949 root_abs_path
1950 );
1951 continue;
1952 }
1953 }
1954 }
1955
1956 let (scan_queue_tx, scan_queue_rx) = channel::unbounded();
1957 for event in events {
1958 let path: Arc<Path> = match event.path.strip_prefix(&root_abs_path) {
1959 Ok(path) => Arc::from(path.to_path_buf()),
1960 Err(_) => {
1961 log::error!(
1962 "unexpected event {:?} for root path {:?}",
1963 event.path,
1964 root_abs_path
1965 );
1966 continue;
1967 }
1968 };
1969
1970 match self.fs.metadata(&event.path).await {
1971 Ok(Some(metadata)) => {
1972 let ignore_stack = snapshot.ignore_stack_for_path(&path, metadata.is_dir);
1973 let mut fs_entry = Entry::new(
1974 path.clone(),
1975 &metadata,
1976 snapshot.next_entry_id.as_ref(),
1977 snapshot.root_char_bag,
1978 );
1979 fs_entry.is_ignored = ignore_stack.is_all();
1980 snapshot.insert_entry(fs_entry, self.fs.as_ref());
1981 if metadata.is_dir {
1982 scan_queue_tx
1983 .send(ScanJob {
1984 abs_path: event.path,
1985 path,
1986 ignore_stack,
1987 scan_queue: scan_queue_tx.clone(),
1988 })
1989 .await
1990 .unwrap();
1991 }
1992 }
1993 Ok(None) => {}
1994 Err(err) => {
1995 // TODO - create a special 'error' entry in the entries tree to mark this
1996 log::error!("error reading file on event {:?}", err);
1997 }
1998 }
1999 }
2000
2001 *self.snapshot.lock() = snapshot;
2002
2003 // Scan any directories that were created as part of this event batch.
2004 drop(scan_queue_tx);
2005 self.executor
2006 .scoped(|scope| {
2007 for _ in 0..self.executor.num_cpus() {
2008 scope.spawn(async {
2009 while let Ok(job) = scan_queue_rx.recv().await {
2010 if let Err(err) = self
2011 .scan_dir(root_char_bag, next_entry_id.clone(), &job)
2012 .await
2013 {
2014 log::error!("error scanning {:?}: {}", job.abs_path, err);
2015 }
2016 }
2017 });
2018 }
2019 })
2020 .await;
2021
2022 // Attempt to detect renames only over a single batch of file-system events.
2023 self.snapshot.lock().removed_entry_ids.clear();
2024
2025 self.update_ignore_statuses().await;
2026 true
2027 }
2028
2029 async fn update_ignore_statuses(&self) {
2030 let mut snapshot = self.snapshot();
2031
2032 let mut ignores_to_update = Vec::new();
2033 let mut ignores_to_delete = Vec::new();
2034 for (parent_path, (_, scan_id)) in &snapshot.ignores {
2035 if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() {
2036 ignores_to_update.push(parent_path.clone());
2037 }
2038
2039 let ignore_path = parent_path.join(&*GITIGNORE);
2040 if snapshot.entry_for_path(ignore_path).is_none() {
2041 ignores_to_delete.push(parent_path.clone());
2042 }
2043 }
2044
2045 for parent_path in ignores_to_delete {
2046 snapshot.ignores.remove(&parent_path);
2047 self.snapshot.lock().ignores.remove(&parent_path);
2048 }
2049
2050 let (ignore_queue_tx, ignore_queue_rx) = channel::unbounded();
2051 ignores_to_update.sort_unstable();
2052 let mut ignores_to_update = ignores_to_update.into_iter().peekable();
2053 while let Some(parent_path) = ignores_to_update.next() {
2054 while ignores_to_update
2055 .peek()
2056 .map_or(false, |p| p.starts_with(&parent_path))
2057 {
2058 ignores_to_update.next().unwrap();
2059 }
2060
2061 let ignore_stack = snapshot.ignore_stack_for_path(&parent_path, true);
2062 ignore_queue_tx
2063 .send(UpdateIgnoreStatusJob {
2064 path: parent_path,
2065 ignore_stack,
2066 ignore_queue: ignore_queue_tx.clone(),
2067 })
2068 .await
2069 .unwrap();
2070 }
2071 drop(ignore_queue_tx);
2072
2073 self.executor
2074 .scoped(|scope| {
2075 for _ in 0..self.executor.num_cpus() {
2076 scope.spawn(async {
2077 while let Ok(job) = ignore_queue_rx.recv().await {
2078 self.update_ignore_status(job, &snapshot).await;
2079 }
2080 });
2081 }
2082 })
2083 .await;
2084 }
2085
2086 async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) {
2087 let mut ignore_stack = job.ignore_stack;
2088 if let Some((ignore, _)) = snapshot.ignores.get(&job.path) {
2089 ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone());
2090 }
2091
2092 let mut entries_by_id_edits = Vec::new();
2093 let mut entries_by_path_edits = Vec::new();
2094 for mut entry in snapshot.child_entries(&job.path).cloned() {
2095 let was_ignored = entry.is_ignored;
2096 entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir());
2097 if entry.is_dir() {
2098 let child_ignore_stack = if entry.is_ignored {
2099 IgnoreStack::all()
2100 } else {
2101 ignore_stack.clone()
2102 };
2103 job.ignore_queue
2104 .send(UpdateIgnoreStatusJob {
2105 path: entry.path.clone(),
2106 ignore_stack: child_ignore_stack,
2107 ignore_queue: job.ignore_queue.clone(),
2108 })
2109 .await
2110 .unwrap();
2111 }
2112
2113 if entry.is_ignored != was_ignored {
2114 let mut path_entry = snapshot.entries_by_id.get(&entry.id, &()).unwrap().clone();
2115 path_entry.scan_id = snapshot.scan_id;
2116 path_entry.is_ignored = entry.is_ignored;
2117 entries_by_id_edits.push(Edit::Insert(path_entry));
2118 entries_by_path_edits.push(Edit::Insert(entry));
2119 }
2120 }
2121
2122 let mut snapshot = self.snapshot.lock();
2123 snapshot.entries_by_path.edit(entries_by_path_edits, &());
2124 snapshot.entries_by_id.edit(entries_by_id_edits, &());
2125 }
2126}
2127
2128async fn refresh_entry(
2129 fs: &dyn Fs,
2130 snapshot: &Mutex<LocalSnapshot>,
2131 path: Arc<Path>,
2132 abs_path: &Path,
2133) -> Result<Entry> {
2134 let root_char_bag;
2135 let next_entry_id;
2136 {
2137 let snapshot = snapshot.lock();
2138 root_char_bag = snapshot.root_char_bag;
2139 next_entry_id = snapshot.next_entry_id.clone();
2140 }
2141 let entry = Entry::new(
2142 path,
2143 &fs.metadata(abs_path)
2144 .await?
2145 .ok_or_else(|| anyhow!("could not read saved file metadata"))?,
2146 &next_entry_id,
2147 root_char_bag,
2148 );
2149 Ok(snapshot.lock().insert_entry(entry, fs))
2150}
2151
2152fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag {
2153 let mut result = root_char_bag;
2154 result.extend(
2155 path.to_string_lossy()
2156 .chars()
2157 .map(|c| c.to_ascii_lowercase()),
2158 );
2159 result
2160}
2161
2162struct ScanJob {
2163 abs_path: PathBuf,
2164 path: Arc<Path>,
2165 ignore_stack: Arc<IgnoreStack>,
2166 scan_queue: Sender<ScanJob>,
2167}
2168
2169struct UpdateIgnoreStatusJob {
2170 path: Arc<Path>,
2171 ignore_stack: Arc<IgnoreStack>,
2172 ignore_queue: Sender<UpdateIgnoreStatusJob>,
2173}
2174
2175pub trait WorktreeHandle {
2176 #[cfg(any(test, feature = "test-support"))]
2177 fn flush_fs_events<'a>(
2178 &self,
2179 cx: &'a gpui::TestAppContext,
2180 ) -> futures::future::LocalBoxFuture<'a, ()>;
2181}
2182
2183impl WorktreeHandle for ModelHandle<Worktree> {
2184 // When the worktree's FS event stream sometimes delivers "redundant" events for FS changes that
2185 // occurred before the worktree was constructed. These events can cause the worktree to perfrom
2186 // extra directory scans, and emit extra scan-state notifications.
2187 //
2188 // This function mutates the worktree's directory and waits for those mutations to be picked up,
2189 // to ensure that all redundant FS events have already been processed.
2190 #[cfg(any(test, feature = "test-support"))]
2191 fn flush_fs_events<'a>(
2192 &self,
2193 cx: &'a gpui::TestAppContext,
2194 ) -> futures::future::LocalBoxFuture<'a, ()> {
2195 use smol::future::FutureExt;
2196
2197 let filename = "fs-event-sentinel";
2198 let tree = self.clone();
2199 let (fs, root_path) = self.read_with(cx, |tree, _| {
2200 let tree = tree.as_local().unwrap();
2201 (tree.fs.clone(), tree.abs_path().clone())
2202 });
2203
2204 async move {
2205 fs.create_file(&root_path.join(filename), Default::default())
2206 .await
2207 .unwrap();
2208 tree.condition(&cx, |tree, _| tree.entry_for_path(filename).is_some())
2209 .await;
2210
2211 fs.remove_file(&root_path.join(filename), Default::default())
2212 .await
2213 .unwrap();
2214 tree.condition(&cx, |tree, _| tree.entry_for_path(filename).is_none())
2215 .await;
2216
2217 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2218 .await;
2219 }
2220 .boxed_local()
2221 }
2222}
2223
2224#[derive(Clone, Debug)]
2225struct TraversalProgress<'a> {
2226 max_path: &'a Path,
2227 count: usize,
2228 visible_count: usize,
2229 file_count: usize,
2230 visible_file_count: usize,
2231}
2232
2233impl<'a> TraversalProgress<'a> {
2234 fn count(&self, include_dirs: bool, include_ignored: bool) -> usize {
2235 match (include_ignored, include_dirs) {
2236 (true, true) => self.count,
2237 (true, false) => self.file_count,
2238 (false, true) => self.visible_count,
2239 (false, false) => self.visible_file_count,
2240 }
2241 }
2242}
2243
2244impl<'a> sum_tree::Dimension<'a, EntrySummary> for TraversalProgress<'a> {
2245 fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
2246 self.max_path = summary.max_path.as_ref();
2247 self.count += summary.count;
2248 self.visible_count += summary.visible_count;
2249 self.file_count += summary.file_count;
2250 self.visible_file_count += summary.visible_file_count;
2251 }
2252}
2253
2254impl<'a> Default for TraversalProgress<'a> {
2255 fn default() -> Self {
2256 Self {
2257 max_path: Path::new(""),
2258 count: 0,
2259 visible_count: 0,
2260 file_count: 0,
2261 visible_file_count: 0,
2262 }
2263 }
2264}
2265
2266pub struct Traversal<'a> {
2267 cursor: sum_tree::Cursor<'a, Entry, TraversalProgress<'a>>,
2268 include_ignored: bool,
2269 include_dirs: bool,
2270}
2271
2272impl<'a> Traversal<'a> {
2273 pub fn advance(&mut self) -> bool {
2274 self.advance_to_offset(self.offset() + 1)
2275 }
2276
2277 pub fn advance_to_offset(&mut self, offset: usize) -> bool {
2278 self.cursor.seek_forward(
2279 &TraversalTarget::Count {
2280 count: offset,
2281 include_dirs: self.include_dirs,
2282 include_ignored: self.include_ignored,
2283 },
2284 Bias::Right,
2285 &(),
2286 )
2287 }
2288
2289 pub fn advance_to_sibling(&mut self) -> bool {
2290 while let Some(entry) = self.cursor.item() {
2291 self.cursor.seek_forward(
2292 &TraversalTarget::PathSuccessor(&entry.path),
2293 Bias::Left,
2294 &(),
2295 );
2296 if let Some(entry) = self.cursor.item() {
2297 if (self.include_dirs || !entry.is_dir())
2298 && (self.include_ignored || !entry.is_ignored)
2299 {
2300 return true;
2301 }
2302 }
2303 }
2304 false
2305 }
2306
2307 pub fn entry(&self) -> Option<&'a Entry> {
2308 self.cursor.item()
2309 }
2310
2311 pub fn offset(&self) -> usize {
2312 self.cursor
2313 .start()
2314 .count(self.include_dirs, self.include_ignored)
2315 }
2316}
2317
2318impl<'a> Iterator for Traversal<'a> {
2319 type Item = &'a Entry;
2320
2321 fn next(&mut self) -> Option<Self::Item> {
2322 if let Some(item) = self.entry() {
2323 self.advance();
2324 Some(item)
2325 } else {
2326 None
2327 }
2328 }
2329}
2330
2331#[derive(Debug)]
2332enum TraversalTarget<'a> {
2333 Path(&'a Path),
2334 PathSuccessor(&'a Path),
2335 Count {
2336 count: usize,
2337 include_ignored: bool,
2338 include_dirs: bool,
2339 },
2340}
2341
2342impl<'a, 'b> SeekTarget<'a, EntrySummary, TraversalProgress<'a>> for TraversalTarget<'b> {
2343 fn cmp(&self, cursor_location: &TraversalProgress<'a>, _: &()) -> Ordering {
2344 match self {
2345 TraversalTarget::Path(path) => path.cmp(&cursor_location.max_path),
2346 TraversalTarget::PathSuccessor(path) => {
2347 if !cursor_location.max_path.starts_with(path) {
2348 Ordering::Equal
2349 } else {
2350 Ordering::Greater
2351 }
2352 }
2353 TraversalTarget::Count {
2354 count,
2355 include_dirs,
2356 include_ignored,
2357 } => Ord::cmp(
2358 count,
2359 &cursor_location.count(*include_dirs, *include_ignored),
2360 ),
2361 }
2362 }
2363}
2364
2365struct ChildEntriesIter<'a> {
2366 parent_path: &'a Path,
2367 traversal: Traversal<'a>,
2368}
2369
2370impl<'a> Iterator for ChildEntriesIter<'a> {
2371 type Item = &'a Entry;
2372
2373 fn next(&mut self) -> Option<Self::Item> {
2374 if let Some(item) = self.traversal.entry() {
2375 if item.path.starts_with(&self.parent_path) {
2376 self.traversal.advance_to_sibling();
2377 return Some(item);
2378 }
2379 }
2380 None
2381 }
2382}
2383
2384impl<'a> From<&'a Entry> for proto::Entry {
2385 fn from(entry: &'a Entry) -> Self {
2386 Self {
2387 id: entry.id as u64,
2388 is_dir: entry.is_dir(),
2389 path: entry.path.to_string_lossy().to_string(),
2390 inode: entry.inode,
2391 mtime: Some(entry.mtime.into()),
2392 is_symlink: entry.is_symlink,
2393 is_ignored: entry.is_ignored,
2394 }
2395 }
2396}
2397
2398impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
2399 type Error = anyhow::Error;
2400
2401 fn try_from((root_char_bag, entry): (&'a CharBag, proto::Entry)) -> Result<Self> {
2402 if let Some(mtime) = entry.mtime {
2403 let kind = if entry.is_dir {
2404 EntryKind::Dir
2405 } else {
2406 let mut char_bag = root_char_bag.clone();
2407 char_bag.extend(entry.path.chars().map(|c| c.to_ascii_lowercase()));
2408 EntryKind::File(char_bag)
2409 };
2410 let path: Arc<Path> = Arc::from(Path::new(&entry.path));
2411 Ok(Entry {
2412 id: entry.id as usize,
2413 kind,
2414 path: path.clone(),
2415 inode: entry.inode,
2416 mtime: mtime.into(),
2417 is_symlink: entry.is_symlink,
2418 is_ignored: entry.is_ignored,
2419 })
2420 } else {
2421 Err(anyhow!(
2422 "missing mtime in remote worktree entry {:?}",
2423 entry.path
2424 ))
2425 }
2426 }
2427}
2428
2429#[cfg(test)]
2430mod tests {
2431 use super::*;
2432 use crate::fs::FakeFs;
2433 use anyhow::Result;
2434 use client::test::FakeHttpClient;
2435 use fs::RealFs;
2436 use rand::prelude::*;
2437 use serde_json::json;
2438 use std::{
2439 env,
2440 fmt::Write,
2441 time::{SystemTime, UNIX_EPOCH},
2442 };
2443 use util::test::temp_tree;
2444
2445 #[gpui::test]
2446 async fn test_traversal(cx: &mut gpui::TestAppContext) {
2447 let fs = FakeFs::new(cx.background());
2448 fs.insert_tree(
2449 "/root",
2450 json!({
2451 ".gitignore": "a/b\n",
2452 "a": {
2453 "b": "",
2454 "c": "",
2455 }
2456 }),
2457 )
2458 .await;
2459
2460 let http_client = FakeHttpClient::with_404_response();
2461 let client = Client::new(http_client);
2462
2463 let tree = Worktree::local(
2464 client,
2465 Arc::from(Path::new("/root")),
2466 false,
2467 fs,
2468 &mut cx.to_async(),
2469 )
2470 .await
2471 .unwrap();
2472 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2473 .await;
2474
2475 tree.read_with(cx, |tree, _| {
2476 assert_eq!(
2477 tree.entries(false)
2478 .map(|entry| entry.path.as_ref())
2479 .collect::<Vec<_>>(),
2480 vec![
2481 Path::new(""),
2482 Path::new(".gitignore"),
2483 Path::new("a"),
2484 Path::new("a/c"),
2485 ]
2486 );
2487 })
2488 }
2489
2490 #[gpui::test]
2491 async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) {
2492 let dir = temp_tree(json!({
2493 ".git": {},
2494 ".gitignore": "ignored-dir\n",
2495 "tracked-dir": {
2496 "tracked-file1": "tracked contents",
2497 },
2498 "ignored-dir": {
2499 "ignored-file1": "ignored contents",
2500 }
2501 }));
2502
2503 let http_client = FakeHttpClient::with_404_response();
2504 let client = Client::new(http_client.clone());
2505
2506 let tree = Worktree::local(
2507 client,
2508 dir.path(),
2509 false,
2510 Arc::new(RealFs),
2511 &mut cx.to_async(),
2512 )
2513 .await
2514 .unwrap();
2515 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2516 .await;
2517 tree.flush_fs_events(&cx).await;
2518 cx.read(|cx| {
2519 let tree = tree.read(cx);
2520 let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap();
2521 let ignored = tree.entry_for_path("ignored-dir/ignored-file1").unwrap();
2522 assert_eq!(tracked.is_ignored, false);
2523 assert_eq!(ignored.is_ignored, true);
2524 });
2525
2526 std::fs::write(dir.path().join("tracked-dir/tracked-file2"), "").unwrap();
2527 std::fs::write(dir.path().join("ignored-dir/ignored-file2"), "").unwrap();
2528 tree.flush_fs_events(&cx).await;
2529 cx.read(|cx| {
2530 let tree = tree.read(cx);
2531 let dot_git = tree.entry_for_path(".git").unwrap();
2532 let tracked = tree.entry_for_path("tracked-dir/tracked-file2").unwrap();
2533 let ignored = tree.entry_for_path("ignored-dir/ignored-file2").unwrap();
2534 assert_eq!(tracked.is_ignored, false);
2535 assert_eq!(ignored.is_ignored, true);
2536 assert_eq!(dot_git.is_ignored, true);
2537 });
2538 }
2539
2540 #[gpui::test(iterations = 100)]
2541 fn test_random(mut rng: StdRng) {
2542 let operations = env::var("OPERATIONS")
2543 .map(|o| o.parse().unwrap())
2544 .unwrap_or(40);
2545 let initial_entries = env::var("INITIAL_ENTRIES")
2546 .map(|o| o.parse().unwrap())
2547 .unwrap_or(20);
2548
2549 let root_dir = tempdir::TempDir::new("worktree-test").unwrap();
2550 for _ in 0..initial_entries {
2551 randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap();
2552 }
2553 log::info!("Generated initial tree");
2554
2555 let (notify_tx, _notify_rx) = mpsc::unbounded();
2556 let fs = Arc::new(RealFs);
2557 let next_entry_id = Arc::new(AtomicUsize::new(0));
2558 let mut initial_snapshot = LocalSnapshot {
2559 abs_path: root_dir.path().into(),
2560 scan_id: 0,
2561 removed_entry_ids: Default::default(),
2562 ignores: Default::default(),
2563 next_entry_id: next_entry_id.clone(),
2564 snapshot: Snapshot {
2565 id: WorktreeId::from_usize(0),
2566 entries_by_path: Default::default(),
2567 entries_by_id: Default::default(),
2568 root_name: Default::default(),
2569 root_char_bag: Default::default(),
2570 },
2571 };
2572 initial_snapshot.insert_entry(
2573 Entry::new(
2574 Path::new("").into(),
2575 &smol::block_on(fs.metadata(root_dir.path()))
2576 .unwrap()
2577 .unwrap(),
2578 &next_entry_id,
2579 Default::default(),
2580 ),
2581 fs.as_ref(),
2582 );
2583 let mut scanner = BackgroundScanner::new(
2584 Arc::new(Mutex::new(initial_snapshot.clone())),
2585 notify_tx,
2586 fs.clone(),
2587 Arc::new(gpui::executor::Background::new()),
2588 );
2589 smol::block_on(scanner.scan_dirs()).unwrap();
2590 scanner.snapshot().check_invariants();
2591
2592 let mut events = Vec::new();
2593 let mut snapshots = Vec::new();
2594 let mut mutations_len = operations;
2595 while mutations_len > 1 {
2596 if !events.is_empty() && rng.gen_bool(0.4) {
2597 let len = rng.gen_range(0..=events.len());
2598 let to_deliver = events.drain(0..len).collect::<Vec<_>>();
2599 log::info!("Delivering events: {:#?}", to_deliver);
2600 smol::block_on(scanner.process_events(to_deliver));
2601 scanner.snapshot().check_invariants();
2602 } else {
2603 events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
2604 mutations_len -= 1;
2605 }
2606
2607 if rng.gen_bool(0.2) {
2608 snapshots.push(scanner.snapshot());
2609 }
2610 }
2611 log::info!("Quiescing: {:#?}", events);
2612 smol::block_on(scanner.process_events(events));
2613 scanner.snapshot().check_invariants();
2614
2615 let (notify_tx, _notify_rx) = mpsc::unbounded();
2616 let mut new_scanner = BackgroundScanner::new(
2617 Arc::new(Mutex::new(initial_snapshot)),
2618 notify_tx,
2619 scanner.fs.clone(),
2620 scanner.executor.clone(),
2621 );
2622 smol::block_on(new_scanner.scan_dirs()).unwrap();
2623 assert_eq!(
2624 scanner.snapshot().to_vec(true),
2625 new_scanner.snapshot().to_vec(true)
2626 );
2627
2628 for mut prev_snapshot in snapshots {
2629 let include_ignored = rng.gen::<bool>();
2630 if !include_ignored {
2631 let mut entries_by_path_edits = Vec::new();
2632 let mut entries_by_id_edits = Vec::new();
2633 for entry in prev_snapshot
2634 .entries_by_id
2635 .cursor::<()>()
2636 .filter(|e| e.is_ignored)
2637 {
2638 entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
2639 entries_by_id_edits.push(Edit::Remove(entry.id));
2640 }
2641
2642 prev_snapshot
2643 .entries_by_path
2644 .edit(entries_by_path_edits, &());
2645 prev_snapshot.entries_by_id.edit(entries_by_id_edits, &());
2646 }
2647
2648 let update = scanner
2649 .snapshot()
2650 .build_update(&prev_snapshot, 0, 0, include_ignored);
2651 prev_snapshot.apply_remote_update(update).unwrap();
2652 assert_eq!(
2653 prev_snapshot.to_vec(true),
2654 scanner.snapshot().to_vec(include_ignored)
2655 );
2656 }
2657 }
2658
2659 fn randomly_mutate_tree(
2660 root_path: &Path,
2661 insertion_probability: f64,
2662 rng: &mut impl Rng,
2663 ) -> Result<Vec<fsevent::Event>> {
2664 let root_path = root_path.canonicalize().unwrap();
2665 let (dirs, files) = read_dir_recursive(root_path.clone());
2666
2667 let mut events = Vec::new();
2668 let mut record_event = |path: PathBuf| {
2669 events.push(fsevent::Event {
2670 event_id: SystemTime::now()
2671 .duration_since(UNIX_EPOCH)
2672 .unwrap()
2673 .as_secs(),
2674 flags: fsevent::StreamFlags::empty(),
2675 path,
2676 });
2677 };
2678
2679 if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
2680 let path = dirs.choose(rng).unwrap();
2681 let new_path = path.join(gen_name(rng));
2682
2683 if rng.gen() {
2684 log::info!("Creating dir {:?}", new_path.strip_prefix(root_path)?);
2685 std::fs::create_dir(&new_path)?;
2686 } else {
2687 log::info!("Creating file {:?}", new_path.strip_prefix(root_path)?);
2688 std::fs::write(&new_path, "")?;
2689 }
2690 record_event(new_path);
2691 } else if rng.gen_bool(0.05) {
2692 let ignore_dir_path = dirs.choose(rng).unwrap();
2693 let ignore_path = ignore_dir_path.join(&*GITIGNORE);
2694
2695 let (subdirs, subfiles) = read_dir_recursive(ignore_dir_path.clone());
2696 let files_to_ignore = {
2697 let len = rng.gen_range(0..=subfiles.len());
2698 subfiles.choose_multiple(rng, len)
2699 };
2700 let dirs_to_ignore = {
2701 let len = rng.gen_range(0..subdirs.len());
2702 subdirs.choose_multiple(rng, len)
2703 };
2704
2705 let mut ignore_contents = String::new();
2706 for path_to_ignore in files_to_ignore.chain(dirs_to_ignore) {
2707 write!(
2708 ignore_contents,
2709 "{}\n",
2710 path_to_ignore
2711 .strip_prefix(&ignore_dir_path)?
2712 .to_str()
2713 .unwrap()
2714 )
2715 .unwrap();
2716 }
2717 log::info!(
2718 "Creating {:?} with contents:\n{}",
2719 ignore_path.strip_prefix(&root_path)?,
2720 ignore_contents
2721 );
2722 std::fs::write(&ignore_path, ignore_contents).unwrap();
2723 record_event(ignore_path);
2724 } else {
2725 let old_path = {
2726 let file_path = files.choose(rng);
2727 let dir_path = dirs[1..].choose(rng);
2728 file_path.into_iter().chain(dir_path).choose(rng).unwrap()
2729 };
2730
2731 let is_rename = rng.gen();
2732 if is_rename {
2733 let new_path_parent = dirs
2734 .iter()
2735 .filter(|d| !d.starts_with(old_path))
2736 .choose(rng)
2737 .unwrap();
2738
2739 let overwrite_existing_dir =
2740 !old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
2741 let new_path = if overwrite_existing_dir {
2742 std::fs::remove_dir_all(&new_path_parent).ok();
2743 new_path_parent.to_path_buf()
2744 } else {
2745 new_path_parent.join(gen_name(rng))
2746 };
2747
2748 log::info!(
2749 "Renaming {:?} to {}{:?}",
2750 old_path.strip_prefix(&root_path)?,
2751 if overwrite_existing_dir {
2752 "overwrite "
2753 } else {
2754 ""
2755 },
2756 new_path.strip_prefix(&root_path)?
2757 );
2758 std::fs::rename(&old_path, &new_path)?;
2759 record_event(old_path.clone());
2760 record_event(new_path);
2761 } else if old_path.is_dir() {
2762 let (dirs, files) = read_dir_recursive(old_path.clone());
2763
2764 log::info!("Deleting dir {:?}", old_path.strip_prefix(&root_path)?);
2765 std::fs::remove_dir_all(&old_path).unwrap();
2766 for file in files {
2767 record_event(file);
2768 }
2769 for dir in dirs {
2770 record_event(dir);
2771 }
2772 } else {
2773 log::info!("Deleting file {:?}", old_path.strip_prefix(&root_path)?);
2774 std::fs::remove_file(old_path).unwrap();
2775 record_event(old_path.clone());
2776 }
2777 }
2778
2779 Ok(events)
2780 }
2781
2782 fn read_dir_recursive(path: PathBuf) -> (Vec<PathBuf>, Vec<PathBuf>) {
2783 let child_entries = std::fs::read_dir(&path).unwrap();
2784 let mut dirs = vec![path];
2785 let mut files = Vec::new();
2786 for child_entry in child_entries {
2787 let child_path = child_entry.unwrap().path();
2788 if child_path.is_dir() {
2789 let (child_dirs, child_files) = read_dir_recursive(child_path);
2790 dirs.extend(child_dirs);
2791 files.extend(child_files);
2792 } else {
2793 files.push(child_path);
2794 }
2795 }
2796 (dirs, files)
2797 }
2798
2799 fn gen_name(rng: &mut impl Rng) -> String {
2800 (0..6)
2801 .map(|_| rng.sample(rand::distributions::Alphanumeric))
2802 .map(char::from)
2803 .collect()
2804 }
2805
2806 impl LocalSnapshot {
2807 fn check_invariants(&self) {
2808 let mut files = self.files(true, 0);
2809 let mut visible_files = self.files(false, 0);
2810 for entry in self.entries_by_path.cursor::<()>() {
2811 if entry.is_file() {
2812 assert_eq!(files.next().unwrap().inode, entry.inode);
2813 if !entry.is_ignored {
2814 assert_eq!(visible_files.next().unwrap().inode, entry.inode);
2815 }
2816 }
2817 }
2818 assert!(files.next().is_none());
2819 assert!(visible_files.next().is_none());
2820
2821 let mut bfs_paths = Vec::new();
2822 let mut stack = vec![Path::new("")];
2823 while let Some(path) = stack.pop() {
2824 bfs_paths.push(path);
2825 let ix = stack.len();
2826 for child_entry in self.child_entries(path) {
2827 stack.insert(ix, &child_entry.path);
2828 }
2829 }
2830
2831 let dfs_paths = self
2832 .entries_by_path
2833 .cursor::<()>()
2834 .map(|e| e.path.as_ref())
2835 .collect::<Vec<_>>();
2836 assert_eq!(bfs_paths, dfs_paths);
2837
2838 for (ignore_parent_path, _) in &self.ignores {
2839 assert!(self.entry_for_path(ignore_parent_path).is_some());
2840 assert!(self
2841 .entry_for_path(ignore_parent_path.join(&*GITIGNORE))
2842 .is_some());
2843 }
2844 }
2845
2846 fn to_vec(&self, include_ignored: bool) -> Vec<(&Path, u64, bool)> {
2847 let mut paths = Vec::new();
2848 for entry in self.entries_by_path.cursor::<()>() {
2849 if include_ignored || !entry.is_ignored {
2850 paths.push((entry.path.as_ref(), entry.inode, entry.is_ignored));
2851 }
2852 }
2853 paths.sort_by(|a, b| a.0.cmp(&b.0));
2854 paths
2855 }
2856 }
2857}