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, PeerId, TypedEnvelope, UserStore};
9use clock::ReplicaId;
10use collections::{hash_map, HashMap};
11use futures::{Stream, StreamExt};
12use fuzzy::CharBag;
13use gpui::{
14 executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
15 Task, UpgradeModelHandle, WeakModelHandle,
16};
17use language::{
18 Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, File as _, Language, LanguageRegistry,
19 Operation, PointUtf16, Rope,
20};
21use lazy_static::lazy_static;
22use lsp::LanguageServer;
23use parking_lot::Mutex;
24use postage::{
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 mem,
38 ops::{Deref, Range},
39 path::{Path, PathBuf},
40 sync::{
41 atomic::{AtomicUsize, Ordering::SeqCst},
42 Arc,
43 },
44 time::{Duration, SystemTime},
45};
46use sum_tree::Bias;
47use sum_tree::{Edit, SeekTarget, SumTree};
48use util::{post_inc, ResultExt, TryFutureExt};
49
50lazy_static! {
51 static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
52}
53
54#[derive(Clone, Debug)]
55enum ScanState {
56 Idle,
57 Scanning,
58 Err(Arc<anyhow::Error>),
59}
60
61pub enum Worktree {
62 Local(LocalWorktree),
63 Remote(RemoteWorktree),
64}
65
66pub enum Event {
67 Closed,
68}
69
70impl Entity for Worktree {
71 type Event = Event;
72
73 fn app_will_quit(
74 &mut self,
75 _: &mut MutableAppContext,
76 ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
77 use futures::FutureExt;
78
79 if let Self::Local(worktree) = self {
80 let shutdown_futures = worktree
81 .language_servers
82 .drain()
83 .filter_map(|(_, server)| server.shutdown())
84 .collect::<Vec<_>>();
85 Some(
86 async move {
87 futures::future::join_all(shutdown_futures).await;
88 }
89 .boxed(),
90 )
91 } else {
92 None
93 }
94 }
95}
96
97impl Worktree {
98 pub async fn open_local(
99 client: Arc<Client>,
100 user_store: ModelHandle<UserStore>,
101 path: impl Into<Arc<Path>>,
102 fs: Arc<dyn Fs>,
103 languages: Arc<LanguageRegistry>,
104 cx: &mut AsyncAppContext,
105 ) -> Result<ModelHandle<Self>> {
106 let (tree, scan_states_tx) =
107 LocalWorktree::new(client, user_store, path, fs.clone(), languages, cx).await?;
108 tree.update(cx, |tree, cx| {
109 let tree = tree.as_local_mut().unwrap();
110 let abs_path = tree.snapshot.abs_path.clone();
111 let background_snapshot = tree.background_snapshot.clone();
112 let background = cx.background().clone();
113 tree._background_scanner_task = Some(cx.background().spawn(async move {
114 let events = fs.watch(&abs_path, Duration::from_millis(100)).await;
115 let scanner =
116 BackgroundScanner::new(background_snapshot, scan_states_tx, fs, background);
117 scanner.run(events).await;
118 }));
119 });
120 Ok(tree)
121 }
122
123 pub async fn remote(
124 project_remote_id: u64,
125 replica_id: ReplicaId,
126 worktree: proto::Worktree,
127 client: Arc<Client>,
128 user_store: ModelHandle<UserStore>,
129 languages: Arc<LanguageRegistry>,
130 cx: &mut AsyncAppContext,
131 ) -> Result<ModelHandle<Self>> {
132 let remote_id = worktree.id;
133 let root_char_bag: CharBag = worktree
134 .root_name
135 .chars()
136 .map(|c| c.to_ascii_lowercase())
137 .collect();
138 let root_name = worktree.root_name.clone();
139 let (entries_by_path, entries_by_id) = cx
140 .background()
141 .spawn(async move {
142 let mut entries_by_path_edits = Vec::new();
143 let mut entries_by_id_edits = Vec::new();
144 for entry in worktree.entries {
145 match Entry::try_from((&root_char_bag, entry)) {
146 Ok(entry) => {
147 entries_by_id_edits.push(Edit::Insert(PathEntry {
148 id: entry.id,
149 path: entry.path.clone(),
150 is_ignored: entry.is_ignored,
151 scan_id: 0,
152 }));
153 entries_by_path_edits.push(Edit::Insert(entry));
154 }
155 Err(err) => log::warn!("error for remote worktree entry {:?}", err),
156 }
157 }
158
159 let mut entries_by_path = SumTree::new();
160 let mut entries_by_id = SumTree::new();
161 entries_by_path.edit(entries_by_path_edits, &());
162 entries_by_id.edit(entries_by_id_edits, &());
163 (entries_by_path, entries_by_id)
164 })
165 .await;
166
167 let worktree = cx.update(|cx| {
168 cx.add_model(|cx: &mut ModelContext<Worktree>| {
169 let snapshot = Snapshot {
170 id: remote_id as usize,
171 scan_id: 0,
172 abs_path: Path::new("").into(),
173 root_name,
174 root_char_bag,
175 ignores: Default::default(),
176 entries_by_path,
177 entries_by_id,
178 removed_entry_ids: Default::default(),
179 next_entry_id: Default::default(),
180 };
181
182 let (updates_tx, mut updates_rx) = postage::mpsc::channel(64);
183 let (mut snapshot_tx, snapshot_rx) = watch::channel_with(snapshot.clone());
184
185 cx.background()
186 .spawn(async move {
187 while let Some(update) = updates_rx.recv().await {
188 let mut snapshot = snapshot_tx.borrow().clone();
189 if let Err(error) = snapshot.apply_update(update) {
190 log::error!("error applying worktree update: {}", error);
191 }
192 *snapshot_tx.borrow_mut() = snapshot;
193 }
194 })
195 .detach();
196
197 {
198 let mut snapshot_rx = snapshot_rx.clone();
199 cx.spawn_weak(|this, mut cx| async move {
200 while let Some(_) = snapshot_rx.recv().await {
201 if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
202 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
203 } else {
204 break;
205 }
206 }
207 })
208 .detach();
209 }
210
211 Worktree::Remote(RemoteWorktree {
212 project_id: project_remote_id,
213 remote_id,
214 replica_id,
215 snapshot,
216 snapshot_rx,
217 updates_tx,
218 client: client.clone(),
219 loading_buffers: Default::default(),
220 open_buffers: Default::default(),
221 diagnostic_summaries: HashMap::default(),
222 queued_operations: Default::default(),
223 languages,
224 user_store,
225 })
226 })
227 });
228
229 Ok(worktree)
230 }
231
232 pub fn as_local(&self) -> Option<&LocalWorktree> {
233 if let Worktree::Local(worktree) = self {
234 Some(worktree)
235 } else {
236 None
237 }
238 }
239
240 pub fn as_remote(&self) -> Option<&RemoteWorktree> {
241 if let Worktree::Remote(worktree) = self {
242 Some(worktree)
243 } else {
244 None
245 }
246 }
247
248 pub fn as_local_mut(&mut self) -> Option<&mut LocalWorktree> {
249 if let Worktree::Local(worktree) = self {
250 Some(worktree)
251 } else {
252 None
253 }
254 }
255
256 pub fn as_remote_mut(&mut self) -> Option<&mut RemoteWorktree> {
257 if let Worktree::Remote(worktree) = self {
258 Some(worktree)
259 } else {
260 None
261 }
262 }
263
264 pub fn snapshot(&self) -> Snapshot {
265 match self {
266 Worktree::Local(worktree) => worktree.snapshot(),
267 Worktree::Remote(worktree) => worktree.snapshot(),
268 }
269 }
270
271 pub fn replica_id(&self) -> ReplicaId {
272 match self {
273 Worktree::Local(_) => 0,
274 Worktree::Remote(worktree) => worktree.replica_id,
275 }
276 }
277
278 pub fn remove_collaborator(
279 &mut self,
280 peer_id: PeerId,
281 replica_id: ReplicaId,
282 cx: &mut ModelContext<Self>,
283 ) {
284 match self {
285 Worktree::Local(worktree) => worktree.remove_collaborator(peer_id, replica_id, cx),
286 Worktree::Remote(worktree) => worktree.remove_collaborator(replica_id, cx),
287 }
288 }
289
290 pub fn languages(&self) -> &Arc<LanguageRegistry> {
291 match self {
292 Worktree::Local(worktree) => &worktree.languages,
293 Worktree::Remote(worktree) => &worktree.languages,
294 }
295 }
296
297 pub fn user_store(&self) -> &ModelHandle<UserStore> {
298 match self {
299 Worktree::Local(worktree) => &worktree.user_store,
300 Worktree::Remote(worktree) => &worktree.user_store,
301 }
302 }
303
304 pub fn handle_open_buffer(
305 &mut self,
306 envelope: TypedEnvelope<proto::OpenBuffer>,
307 rpc: Arc<Client>,
308 cx: &mut ModelContext<Self>,
309 ) -> anyhow::Result<()> {
310 let receipt = envelope.receipt();
311
312 let response = self
313 .as_local_mut()
314 .unwrap()
315 .open_remote_buffer(envelope, cx);
316
317 cx.background()
318 .spawn(
319 async move {
320 rpc.respond(receipt, response.await?).await?;
321 Ok(())
322 }
323 .log_err(),
324 )
325 .detach();
326
327 Ok(())
328 }
329
330 pub fn handle_close_buffer(
331 &mut self,
332 envelope: TypedEnvelope<proto::CloseBuffer>,
333 _: Arc<Client>,
334 cx: &mut ModelContext<Self>,
335 ) -> anyhow::Result<()> {
336 self.as_local_mut()
337 .unwrap()
338 .close_remote_buffer(envelope, cx)
339 }
340
341 pub fn diagnostic_summaries<'a>(
342 &'a self,
343 ) -> impl Iterator<Item = (Arc<Path>, DiagnosticSummary)> + 'a {
344 match self {
345 Worktree::Local(worktree) => &worktree.diagnostic_summaries,
346 Worktree::Remote(worktree) => &worktree.diagnostic_summaries,
347 }
348 .iter()
349 .map(|(path, summary)| (path.clone(), summary.clone()))
350 }
351
352 pub fn loading_buffers<'a>(&'a mut self) -> &'a mut LoadingBuffers {
353 match self {
354 Worktree::Local(worktree) => &mut worktree.loading_buffers,
355 Worktree::Remote(worktree) => &mut worktree.loading_buffers,
356 }
357 }
358
359 pub fn open_buffer(
360 &mut self,
361 path: impl AsRef<Path>,
362 cx: &mut ModelContext<Self>,
363 ) -> Task<Result<ModelHandle<Buffer>>> {
364 let path = path.as_ref();
365
366 // If there is already a buffer for the given path, then return it.
367 let existing_buffer = match self {
368 Worktree::Local(worktree) => worktree.get_open_buffer(path, cx),
369 Worktree::Remote(worktree) => worktree.get_open_buffer(path, cx),
370 };
371 if let Some(existing_buffer) = existing_buffer {
372 return cx.spawn(move |_, _| async move { Ok(existing_buffer) });
373 }
374
375 let path: Arc<Path> = Arc::from(path);
376 let mut loading_watch = match self.loading_buffers().entry(path.clone()) {
377 // If the given path is already being loaded, then wait for that existing
378 // task to complete and return the same buffer.
379 hash_map::Entry::Occupied(e) => e.get().clone(),
380
381 // Otherwise, record the fact that this path is now being loaded.
382 hash_map::Entry::Vacant(entry) => {
383 let (mut tx, rx) = postage::watch::channel();
384 entry.insert(rx.clone());
385
386 let load_buffer = match self {
387 Worktree::Local(worktree) => worktree.open_buffer(&path, cx),
388 Worktree::Remote(worktree) => worktree.open_buffer(&path, cx),
389 };
390 cx.spawn(move |this, mut cx| async move {
391 let result = load_buffer.await;
392
393 // After the buffer loads, record the fact that it is no longer
394 // loading.
395 this.update(&mut cx, |this, _| this.loading_buffers().remove(&path));
396 *tx.borrow_mut() = Some(result.map_err(|e| Arc::new(e)));
397 })
398 .detach();
399 rx
400 }
401 };
402
403 cx.spawn(|_, _| async move {
404 loop {
405 if let Some(result) = loading_watch.borrow().as_ref() {
406 return result.clone().map_err(|e| anyhow!("{}", e));
407 }
408 loading_watch.recv().await;
409 }
410 })
411 }
412
413 #[cfg(feature = "test-support")]
414 pub fn has_open_buffer(&self, path: impl AsRef<Path>, cx: &AppContext) -> bool {
415 let mut open_buffers: Box<dyn Iterator<Item = _>> = match self {
416 Worktree::Local(worktree) => Box::new(worktree.open_buffers.values()),
417 Worktree::Remote(worktree) => {
418 Box::new(worktree.open_buffers.values().filter_map(|buf| {
419 if let RemoteBuffer::Loaded(buf) = buf {
420 Some(buf)
421 } else {
422 None
423 }
424 }))
425 }
426 };
427
428 let path = path.as_ref();
429 open_buffers
430 .find(|buffer| {
431 if let Some(file) = buffer.upgrade(cx).and_then(|buffer| buffer.read(cx).file()) {
432 file.path().as_ref() == path
433 } else {
434 false
435 }
436 })
437 .is_some()
438 }
439
440 pub fn handle_update_buffer(
441 &mut self,
442 envelope: TypedEnvelope<proto::UpdateBuffer>,
443 cx: &mut ModelContext<Self>,
444 ) -> Result<()> {
445 let payload = envelope.payload.clone();
446 let buffer_id = payload.buffer_id as usize;
447 let ops = payload
448 .operations
449 .into_iter()
450 .map(|op| language::proto::deserialize_operation(op))
451 .collect::<Result<Vec<_>, _>>()?;
452
453 match self {
454 Worktree::Local(worktree) => {
455 let buffer = worktree
456 .open_buffers
457 .get(&buffer_id)
458 .and_then(|buf| buf.upgrade(cx))
459 .ok_or_else(|| {
460 anyhow!("invalid buffer {} in update buffer message", buffer_id)
461 })?;
462 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
463 }
464 Worktree::Remote(worktree) => match worktree.open_buffers.get_mut(&buffer_id) {
465 Some(RemoteBuffer::Operations(pending_ops)) => pending_ops.extend(ops),
466 Some(RemoteBuffer::Loaded(buffer)) => {
467 if let Some(buffer) = buffer.upgrade(cx) {
468 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
469 } else {
470 worktree
471 .open_buffers
472 .insert(buffer_id, RemoteBuffer::Operations(ops));
473 }
474 }
475 None => {
476 worktree
477 .open_buffers
478 .insert(buffer_id, RemoteBuffer::Operations(ops));
479 }
480 },
481 }
482
483 Ok(())
484 }
485
486 pub fn handle_save_buffer(
487 &mut self,
488 envelope: TypedEnvelope<proto::SaveBuffer>,
489 rpc: Arc<Client>,
490 cx: &mut ModelContext<Self>,
491 ) -> Result<()> {
492 let sender_id = envelope.original_sender_id()?;
493 let this = self.as_local().unwrap();
494 let project_id = this
495 .share
496 .as_ref()
497 .ok_or_else(|| anyhow!("can't save buffer while disconnected"))?
498 .project_id;
499
500 let buffer = this
501 .shared_buffers
502 .get(&sender_id)
503 .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
504 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
505
506 let receipt = envelope.receipt();
507 let worktree_id = envelope.payload.worktree_id;
508 let buffer_id = envelope.payload.buffer_id;
509 let save = cx.spawn(|_, mut cx| async move {
510 buffer.update(&mut cx, |buffer, cx| buffer.save(cx))?.await
511 });
512
513 cx.background()
514 .spawn(
515 async move {
516 let (version, mtime) = save.await?;
517
518 rpc.respond(
519 receipt,
520 proto::BufferSaved {
521 project_id,
522 worktree_id,
523 buffer_id,
524 version: (&version).into(),
525 mtime: Some(mtime.into()),
526 },
527 )
528 .await?;
529
530 Ok(())
531 }
532 .log_err(),
533 )
534 .detach();
535
536 Ok(())
537 }
538
539 pub fn handle_buffer_saved(
540 &mut self,
541 envelope: TypedEnvelope<proto::BufferSaved>,
542 cx: &mut ModelContext<Self>,
543 ) -> Result<()> {
544 let payload = envelope.payload.clone();
545 let worktree = self.as_remote_mut().unwrap();
546 if let Some(buffer) = worktree
547 .open_buffers
548 .get(&(payload.buffer_id as usize))
549 .and_then(|buf| buf.upgrade(cx))
550 {
551 buffer.update(cx, |buffer, cx| {
552 let version = payload.version.try_into()?;
553 let mtime = payload
554 .mtime
555 .ok_or_else(|| anyhow!("missing mtime"))?
556 .into();
557 buffer.did_save(version, mtime, None, cx);
558 Result::<_, anyhow::Error>::Ok(())
559 })?;
560 }
561 Ok(())
562 }
563
564 fn poll_snapshot(&mut self, cx: &mut ModelContext<Self>) {
565 match self {
566 Self::Local(worktree) => {
567 let is_fake_fs = worktree.fs.is_fake();
568 worktree.snapshot = worktree.background_snapshot.lock().clone();
569 if worktree.is_scanning() {
570 if worktree.poll_task.is_none() {
571 worktree.poll_task = Some(cx.spawn(|this, mut cx| async move {
572 if is_fake_fs {
573 smol::future::yield_now().await;
574 } else {
575 smol::Timer::after(Duration::from_millis(100)).await;
576 }
577 this.update(&mut cx, |this, cx| {
578 this.as_local_mut().unwrap().poll_task = None;
579 this.poll_snapshot(cx);
580 })
581 }));
582 }
583 } else {
584 worktree.poll_task.take();
585 self.update_open_buffers(cx);
586 }
587 }
588 Self::Remote(worktree) => {
589 worktree.snapshot = worktree.snapshot_rx.borrow().clone();
590 self.update_open_buffers(cx);
591 }
592 };
593
594 cx.notify();
595 }
596
597 fn update_open_buffers(&mut self, cx: &mut ModelContext<Self>) {
598 let open_buffers: Box<dyn Iterator<Item = _>> = match &self {
599 Self::Local(worktree) => Box::new(worktree.open_buffers.iter()),
600 Self::Remote(worktree) => {
601 Box::new(worktree.open_buffers.iter().filter_map(|(id, buf)| {
602 if let RemoteBuffer::Loaded(buf) = buf {
603 Some((id, buf))
604 } else {
605 None
606 }
607 }))
608 }
609 };
610
611 let local = self.as_local().is_some();
612 let worktree_path = self.abs_path.clone();
613 let worktree_handle = cx.handle();
614 let mut buffers_to_delete = Vec::new();
615 for (buffer_id, buffer) in open_buffers {
616 if let Some(buffer) = buffer.upgrade(cx) {
617 buffer.update(cx, |buffer, cx| {
618 if let Some(old_file) = buffer.file() {
619 let new_file = if let Some(entry) = old_file
620 .entry_id()
621 .and_then(|entry_id| self.entry_for_id(entry_id))
622 {
623 File {
624 is_local: local,
625 worktree_path: worktree_path.clone(),
626 entry_id: Some(entry.id),
627 mtime: entry.mtime,
628 path: entry.path.clone(),
629 worktree: worktree_handle.clone(),
630 }
631 } else if let Some(entry) = self.entry_for_path(old_file.path().as_ref()) {
632 File {
633 is_local: local,
634 worktree_path: worktree_path.clone(),
635 entry_id: Some(entry.id),
636 mtime: entry.mtime,
637 path: entry.path.clone(),
638 worktree: worktree_handle.clone(),
639 }
640 } else {
641 File {
642 is_local: local,
643 worktree_path: worktree_path.clone(),
644 entry_id: None,
645 path: old_file.path().clone(),
646 mtime: old_file.mtime(),
647 worktree: worktree_handle.clone(),
648 }
649 };
650
651 if let Some(task) = buffer.file_updated(Box::new(new_file), cx) {
652 task.detach();
653 }
654 }
655 });
656 } else {
657 buffers_to_delete.push(*buffer_id);
658 }
659 }
660
661 for buffer_id in buffers_to_delete {
662 match self {
663 Self::Local(worktree) => {
664 worktree.open_buffers.remove(&buffer_id);
665 }
666 Self::Remote(worktree) => {
667 worktree.open_buffers.remove(&buffer_id);
668 }
669 }
670 }
671 }
672
673 fn update_diagnostics(
674 &mut self,
675 mut params: lsp::PublishDiagnosticsParams,
676 cx: &mut ModelContext<Worktree>,
677 ) -> Result<()> {
678 let this = self.as_local_mut().ok_or_else(|| anyhow!("not local"))?;
679 let abs_path = params
680 .uri
681 .to_file_path()
682 .map_err(|_| anyhow!("URI is not a file"))?;
683 let worktree_path = Arc::from(
684 abs_path
685 .strip_prefix(&this.abs_path)
686 .context("path is not within worktree")?,
687 );
688
689 let mut group_ids_by_diagnostic_range = HashMap::default();
690 let mut diagnostics_by_group_id = HashMap::default();
691 let mut next_group_id = 0;
692 for diagnostic in &mut params.diagnostics {
693 let source = diagnostic.source.as_ref();
694 let code = diagnostic.code.as_ref();
695 let group_id = diagnostic_ranges(&diagnostic, &abs_path)
696 .find_map(|range| group_ids_by_diagnostic_range.get(&(source, code, range)))
697 .copied()
698 .unwrap_or_else(|| {
699 let group_id = post_inc(&mut next_group_id);
700 for range in diagnostic_ranges(&diagnostic, &abs_path) {
701 group_ids_by_diagnostic_range.insert((source, code, range), group_id);
702 }
703 group_id
704 });
705
706 diagnostics_by_group_id
707 .entry(group_id)
708 .or_insert(Vec::new())
709 .push(DiagnosticEntry {
710 range: diagnostic.range.start.to_point_utf16()
711 ..diagnostic.range.end.to_point_utf16(),
712 diagnostic: Diagnostic {
713 source: diagnostic.source.clone(),
714 code: diagnostic.code.clone().map(|code| match code {
715 lsp::NumberOrString::Number(code) => code.to_string(),
716 lsp::NumberOrString::String(code) => code,
717 }),
718 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
719 message: mem::take(&mut diagnostic.message),
720 group_id,
721 is_primary: false,
722 },
723 });
724 }
725
726 let diagnostics = diagnostics_by_group_id
727 .into_values()
728 .flat_map(|mut diagnostics| {
729 let primary = diagnostics
730 .iter_mut()
731 .min_by_key(|entry| entry.diagnostic.severity)
732 .unwrap();
733 primary.diagnostic.is_primary = true;
734 diagnostics
735 })
736 .collect::<Vec<_>>();
737
738 for buffer in this.open_buffers.values() {
739 if let Some(buffer) = buffer.upgrade(cx) {
740 if buffer
741 .read(cx)
742 .file()
743 .map_or(false, |file| *file.path() == worktree_path)
744 {
745 let (remote_id, operation) = buffer.update(cx, |buffer, cx| {
746 (
747 buffer.remote_id(),
748 buffer.update_diagnostics(params.version, diagnostics.clone(), cx),
749 )
750 });
751 self.send_buffer_update(remote_id, operation?, cx);
752 break;
753 }
754 }
755 }
756
757 let this = self.as_local_mut().unwrap();
758 this.diagnostic_summaries
759 .insert(worktree_path.clone(), DiagnosticSummary::new(&diagnostics));
760 this.diagnostics.insert(worktree_path.clone(), diagnostics);
761 Ok(())
762 }
763
764 fn send_buffer_update(
765 &mut self,
766 buffer_id: u64,
767 operation: Operation,
768 cx: &mut ModelContext<Self>,
769 ) {
770 if let Some((project_id, worktree_id, rpc)) = match self {
771 Worktree::Local(worktree) => worktree.share.as_ref().map(|share| {
772 (
773 share.project_id,
774 worktree.id() as u64,
775 worktree.client.clone(),
776 )
777 }),
778 Worktree::Remote(worktree) => Some((
779 worktree.project_id,
780 worktree.remote_id,
781 worktree.client.clone(),
782 )),
783 } {
784 cx.spawn(|worktree, mut cx| async move {
785 if let Err(error) = rpc
786 .request(proto::UpdateBuffer {
787 project_id,
788 worktree_id,
789 buffer_id,
790 operations: vec![language::proto::serialize_operation(&operation)],
791 })
792 .await
793 {
794 worktree.update(&mut cx, |worktree, _| {
795 log::error!("error sending buffer operation: {}", error);
796 match worktree {
797 Worktree::Local(t) => &mut t.queued_operations,
798 Worktree::Remote(t) => &mut t.queued_operations,
799 }
800 .push((buffer_id, operation));
801 });
802 }
803 })
804 .detach();
805 }
806 }
807}
808
809#[derive(Clone)]
810pub struct Snapshot {
811 id: usize,
812 scan_id: usize,
813 abs_path: Arc<Path>,
814 root_name: String,
815 root_char_bag: CharBag,
816 ignores: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
817 entries_by_path: SumTree<Entry>,
818 entries_by_id: SumTree<PathEntry>,
819 removed_entry_ids: HashMap<u64, usize>,
820 next_entry_id: Arc<AtomicUsize>,
821}
822
823pub struct LocalWorktree {
824 snapshot: Snapshot,
825 config: WorktreeConfig,
826 background_snapshot: Arc<Mutex<Snapshot>>,
827 last_scan_state_rx: watch::Receiver<ScanState>,
828 _background_scanner_task: Option<Task<()>>,
829 poll_task: Option<Task<()>>,
830 share: Option<ShareState>,
831 loading_buffers: LoadingBuffers,
832 open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
833 shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
834 diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<PointUtf16>>>,
835 diagnostic_summaries: HashMap<Arc<Path>, DiagnosticSummary>,
836 queued_operations: Vec<(u64, Operation)>,
837 languages: Arc<LanguageRegistry>,
838 client: Arc<Client>,
839 user_store: ModelHandle<UserStore>,
840 fs: Arc<dyn Fs>,
841 language_servers: HashMap<String, Arc<LanguageServer>>,
842}
843
844struct ShareState {
845 project_id: u64,
846 snapshots_tx: Sender<Snapshot>,
847}
848
849pub struct RemoteWorktree {
850 project_id: u64,
851 remote_id: u64,
852 snapshot: Snapshot,
853 snapshot_rx: watch::Receiver<Snapshot>,
854 client: Arc<Client>,
855 updates_tx: postage::mpsc::Sender<proto::UpdateWorktree>,
856 replica_id: ReplicaId,
857 loading_buffers: LoadingBuffers,
858 open_buffers: HashMap<usize, RemoteBuffer>,
859 diagnostic_summaries: HashMap<Arc<Path>, DiagnosticSummary>,
860 languages: Arc<LanguageRegistry>,
861 user_store: ModelHandle<UserStore>,
862 queued_operations: Vec<(u64, Operation)>,
863}
864
865type LoadingBuffers = HashMap<
866 Arc<Path>,
867 postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
868>;
869
870#[derive(Default, Deserialize)]
871struct WorktreeConfig {
872 collaborators: Vec<String>,
873}
874
875impl LocalWorktree {
876 async fn new(
877 client: Arc<Client>,
878 user_store: ModelHandle<UserStore>,
879 path: impl Into<Arc<Path>>,
880 fs: Arc<dyn Fs>,
881 languages: Arc<LanguageRegistry>,
882 cx: &mut AsyncAppContext,
883 ) -> Result<(ModelHandle<Worktree>, Sender<ScanState>)> {
884 let abs_path = path.into();
885 let path: Arc<Path> = Arc::from(Path::new(""));
886 let next_entry_id = AtomicUsize::new(0);
887
888 // After determining whether the root entry is a file or a directory, populate the
889 // snapshot's "root name", which will be used for the purpose of fuzzy matching.
890 let root_name = abs_path
891 .file_name()
892 .map_or(String::new(), |f| f.to_string_lossy().to_string());
893 let root_char_bag = root_name.chars().map(|c| c.to_ascii_lowercase()).collect();
894 let metadata = fs.metadata(&abs_path).await?;
895
896 let mut config = WorktreeConfig::default();
897 if let Ok(zed_toml) = fs.load(&abs_path.join(".zed.toml")).await {
898 if let Ok(parsed) = toml::from_str(&zed_toml) {
899 config = parsed;
900 }
901 }
902
903 let (scan_states_tx, scan_states_rx) = smol::channel::unbounded();
904 let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning);
905 let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| {
906 let mut snapshot = Snapshot {
907 id: cx.model_id(),
908 scan_id: 0,
909 abs_path,
910 root_name: root_name.clone(),
911 root_char_bag,
912 ignores: Default::default(),
913 entries_by_path: Default::default(),
914 entries_by_id: Default::default(),
915 removed_entry_ids: Default::default(),
916 next_entry_id: Arc::new(next_entry_id),
917 };
918 if let Some(metadata) = metadata {
919 snapshot.insert_entry(
920 Entry::new(
921 path.into(),
922 &metadata,
923 &snapshot.next_entry_id,
924 snapshot.root_char_bag,
925 ),
926 fs.as_ref(),
927 );
928 }
929
930 let tree = Self {
931 snapshot: snapshot.clone(),
932 config,
933 background_snapshot: Arc::new(Mutex::new(snapshot)),
934 last_scan_state_rx,
935 _background_scanner_task: None,
936 share: None,
937 poll_task: None,
938 loading_buffers: Default::default(),
939 open_buffers: Default::default(),
940 shared_buffers: Default::default(),
941 diagnostics: Default::default(),
942 diagnostic_summaries: Default::default(),
943 queued_operations: Default::default(),
944 languages,
945 client,
946 user_store,
947 fs,
948 language_servers: Default::default(),
949 };
950
951 cx.spawn_weak(|this, mut cx| async move {
952 while let Ok(scan_state) = scan_states_rx.recv().await {
953 if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
954 let to_send = handle.update(&mut cx, |this, cx| {
955 last_scan_state_tx.blocking_send(scan_state).ok();
956 this.poll_snapshot(cx);
957 let tree = this.as_local_mut().unwrap();
958 if !tree.is_scanning() {
959 if let Some(share) = tree.share.as_ref() {
960 return Some((tree.snapshot(), share.snapshots_tx.clone()));
961 }
962 }
963 None
964 });
965
966 if let Some((snapshot, snapshots_to_send_tx)) = to_send {
967 if let Err(err) = snapshots_to_send_tx.send(snapshot).await {
968 log::error!("error submitting snapshot to send {}", err);
969 }
970 }
971 } else {
972 break;
973 }
974 }
975 })
976 .detach();
977
978 Worktree::Local(tree)
979 });
980
981 Ok((tree, scan_states_tx))
982 }
983
984 pub fn authorized_logins(&self) -> Vec<String> {
985 self.config.collaborators.clone()
986 }
987
988 pub fn languages(&self) -> &LanguageRegistry {
989 &self.languages
990 }
991
992 pub fn ensure_language_server(
993 &mut self,
994 language: &Language,
995 cx: &mut ModelContext<Worktree>,
996 ) -> Option<Arc<LanguageServer>> {
997 if let Some(server) = self.language_servers.get(language.name()) {
998 return Some(server.clone());
999 }
1000
1001 if let Some(language_server) = language
1002 .start_server(self.abs_path(), cx)
1003 .log_err()
1004 .flatten()
1005 {
1006 let (diagnostics_tx, diagnostics_rx) = smol::channel::unbounded();
1007 language_server
1008 .on_notification::<lsp::notification::PublishDiagnostics, _>(move |params| {
1009 smol::block_on(diagnostics_tx.send(params)).ok();
1010 })
1011 .detach();
1012 cx.spawn_weak(|this, mut cx| async move {
1013 while let Ok(diagnostics) = diagnostics_rx.recv().await {
1014 if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
1015 handle.update(&mut cx, |this, cx| {
1016 this.update_diagnostics(diagnostics, cx).log_err();
1017 });
1018 } else {
1019 break;
1020 }
1021 }
1022 })
1023 .detach();
1024
1025 self.language_servers
1026 .insert(language.name().to_string(), language_server.clone());
1027 Some(language_server.clone())
1028 } else {
1029 None
1030 }
1031 }
1032
1033 fn get_open_buffer(
1034 &mut self,
1035 path: &Path,
1036 cx: &mut ModelContext<Worktree>,
1037 ) -> Option<ModelHandle<Buffer>> {
1038 let worktree_id = self.id();
1039 let mut result = None;
1040 self.open_buffers.retain(|_buffer_id, buffer| {
1041 if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
1042 if let Some(file) = buffer.read(cx.as_ref()).file() {
1043 if file.worktree_id() == worktree_id && file.path().as_ref() == path {
1044 result = Some(buffer);
1045 }
1046 }
1047 true
1048 } else {
1049 false
1050 }
1051 });
1052 result
1053 }
1054
1055 fn open_buffer(
1056 &mut self,
1057 path: &Path,
1058 cx: &mut ModelContext<Worktree>,
1059 ) -> Task<Result<ModelHandle<Buffer>>> {
1060 let path = Arc::from(path);
1061 cx.spawn(move |this, mut cx| async move {
1062 let (file, contents) = this
1063 .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
1064 .await?;
1065
1066 let (diagnostics, language, language_server) = this.update(&mut cx, |this, cx| {
1067 let this = this.as_local_mut().unwrap();
1068 let diagnostics = this.diagnostics.remove(&path);
1069 let language = this.languages.select_language(file.full_path()).cloned();
1070 let server = language
1071 .as_ref()
1072 .and_then(|language| this.ensure_language_server(language, cx));
1073 (diagnostics, language, server)
1074 });
1075
1076 let buffer = cx.add_model(|cx| {
1077 let mut buffer = Buffer::from_file(0, contents, Box::new(file), cx);
1078 buffer.set_language(language, language_server, cx);
1079 if let Some(diagnostics) = diagnostics {
1080 buffer.update_diagnostics(None, diagnostics, cx).unwrap();
1081 }
1082 buffer
1083 });
1084
1085 this.update(&mut cx, |this, _| {
1086 let this = this.as_local_mut().unwrap();
1087 this.open_buffers.insert(buffer.id(), buffer.downgrade());
1088 });
1089
1090 Ok(buffer)
1091 })
1092 }
1093
1094 pub fn open_remote_buffer(
1095 &mut self,
1096 envelope: TypedEnvelope<proto::OpenBuffer>,
1097 cx: &mut ModelContext<Worktree>,
1098 ) -> Task<Result<proto::OpenBufferResponse>> {
1099 cx.spawn(|this, mut cx| async move {
1100 let peer_id = envelope.original_sender_id();
1101 let path = Path::new(&envelope.payload.path);
1102 let buffer = this
1103 .update(&mut cx, |this, cx| this.open_buffer(path, cx))
1104 .await?;
1105 this.update(&mut cx, |this, cx| {
1106 this.as_local_mut()
1107 .unwrap()
1108 .shared_buffers
1109 .entry(peer_id?)
1110 .or_default()
1111 .insert(buffer.id() as u64, buffer.clone());
1112
1113 Ok(proto::OpenBufferResponse {
1114 buffer: Some(buffer.update(cx.as_mut(), |buffer, _| buffer.to_proto())),
1115 })
1116 })
1117 })
1118 }
1119
1120 pub fn close_remote_buffer(
1121 &mut self,
1122 envelope: TypedEnvelope<proto::CloseBuffer>,
1123 cx: &mut ModelContext<Worktree>,
1124 ) -> Result<()> {
1125 if let Some(shared_buffers) = self.shared_buffers.get_mut(&envelope.original_sender_id()?) {
1126 shared_buffers.remove(&envelope.payload.buffer_id);
1127 cx.notify();
1128 }
1129
1130 Ok(())
1131 }
1132
1133 pub fn remove_collaborator(
1134 &mut self,
1135 peer_id: PeerId,
1136 replica_id: ReplicaId,
1137 cx: &mut ModelContext<Worktree>,
1138 ) {
1139 self.shared_buffers.remove(&peer_id);
1140 for (_, buffer) in &self.open_buffers {
1141 if let Some(buffer) = buffer.upgrade(cx) {
1142 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
1143 }
1144 }
1145 cx.notify();
1146 }
1147
1148 pub fn scan_complete(&self) -> impl Future<Output = ()> {
1149 let mut scan_state_rx = self.last_scan_state_rx.clone();
1150 async move {
1151 let mut scan_state = Some(scan_state_rx.borrow().clone());
1152 while let Some(ScanState::Scanning) = scan_state {
1153 scan_state = scan_state_rx.recv().await;
1154 }
1155 }
1156 }
1157
1158 fn is_scanning(&self) -> bool {
1159 if let ScanState::Scanning = *self.last_scan_state_rx.borrow() {
1160 true
1161 } else {
1162 false
1163 }
1164 }
1165
1166 pub fn snapshot(&self) -> Snapshot {
1167 self.snapshot.clone()
1168 }
1169
1170 pub fn abs_path(&self) -> &Path {
1171 self.snapshot.abs_path.as_ref()
1172 }
1173
1174 pub fn contains_abs_path(&self, path: &Path) -> bool {
1175 path.starts_with(&self.snapshot.abs_path)
1176 }
1177
1178 fn absolutize(&self, path: &Path) -> PathBuf {
1179 if path.file_name().is_some() {
1180 self.snapshot.abs_path.join(path)
1181 } else {
1182 self.snapshot.abs_path.to_path_buf()
1183 }
1184 }
1185
1186 fn load(&self, path: &Path, cx: &mut ModelContext<Worktree>) -> Task<Result<(File, String)>> {
1187 let handle = cx.handle();
1188 let path = Arc::from(path);
1189 let worktree_path = self.abs_path.clone();
1190 let abs_path = self.absolutize(&path);
1191 let background_snapshot = self.background_snapshot.clone();
1192 let fs = self.fs.clone();
1193 cx.spawn(|this, mut cx| async move {
1194 let text = fs.load(&abs_path).await?;
1195 // Eagerly populate the snapshot with an updated entry for the loaded file
1196 let entry = refresh_entry(fs.as_ref(), &background_snapshot, path, &abs_path).await?;
1197 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
1198 Ok((
1199 File {
1200 entry_id: Some(entry.id),
1201 worktree: handle,
1202 worktree_path,
1203 path: entry.path,
1204 mtime: entry.mtime,
1205 is_local: true,
1206 },
1207 text,
1208 ))
1209 })
1210 }
1211
1212 pub fn save_buffer_as(
1213 &self,
1214 buffer: ModelHandle<Buffer>,
1215 path: impl Into<Arc<Path>>,
1216 text: Rope,
1217 cx: &mut ModelContext<Worktree>,
1218 ) -> Task<Result<File>> {
1219 let save = self.save(path, text, cx);
1220 cx.spawn(|this, mut cx| async move {
1221 let entry = save.await?;
1222 this.update(&mut cx, |this, cx| {
1223 let this = this.as_local_mut().unwrap();
1224 this.open_buffers.insert(buffer.id(), buffer.downgrade());
1225 Ok(File {
1226 entry_id: Some(entry.id),
1227 worktree: cx.handle(),
1228 worktree_path: this.abs_path.clone(),
1229 path: entry.path,
1230 mtime: entry.mtime,
1231 is_local: true,
1232 })
1233 })
1234 })
1235 }
1236
1237 fn save(
1238 &self,
1239 path: impl Into<Arc<Path>>,
1240 text: Rope,
1241 cx: &mut ModelContext<Worktree>,
1242 ) -> Task<Result<Entry>> {
1243 let path = path.into();
1244 let abs_path = self.absolutize(&path);
1245 let background_snapshot = self.background_snapshot.clone();
1246 let fs = self.fs.clone();
1247 let save = cx.background().spawn(async move {
1248 fs.save(&abs_path, &text).await?;
1249 refresh_entry(fs.as_ref(), &background_snapshot, path.clone(), &abs_path).await
1250 });
1251
1252 cx.spawn(|this, mut cx| async move {
1253 let entry = save.await?;
1254 this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
1255 Ok(entry)
1256 })
1257 }
1258
1259 pub fn share(
1260 &mut self,
1261 project_id: u64,
1262 cx: &mut ModelContext<Worktree>,
1263 ) -> Task<anyhow::Result<()>> {
1264 if self.share.is_some() {
1265 return Task::ready(Ok(()));
1266 }
1267
1268 let snapshot = self.snapshot();
1269 let rpc = self.client.clone();
1270 let worktree_id = cx.model_id() as u64;
1271 let (snapshots_to_send_tx, snapshots_to_send_rx) = smol::channel::unbounded::<Snapshot>();
1272 self.share = Some(ShareState {
1273 project_id,
1274 snapshots_tx: snapshots_to_send_tx,
1275 });
1276
1277 cx.background()
1278 .spawn({
1279 let rpc = rpc.clone();
1280 let snapshot = snapshot.clone();
1281 async move {
1282 let mut prev_snapshot = snapshot;
1283 while let Ok(snapshot) = snapshots_to_send_rx.recv().await {
1284 let message =
1285 snapshot.build_update(&prev_snapshot, project_id, worktree_id, false);
1286 match rpc.send(message).await {
1287 Ok(()) => prev_snapshot = snapshot,
1288 Err(err) => log::error!("error sending snapshot diff {}", err),
1289 }
1290 }
1291 }
1292 })
1293 .detach();
1294
1295 let share_message = cx.background().spawn(async move {
1296 proto::ShareWorktree {
1297 project_id,
1298 worktree: Some(snapshot.to_proto()),
1299 }
1300 });
1301
1302 cx.foreground().spawn(async move {
1303 rpc.request(share_message.await).await?;
1304 Ok(())
1305 })
1306 }
1307}
1308
1309fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
1310 let contents = smol::block_on(fs.load(&abs_path))?;
1311 let parent = abs_path.parent().unwrap_or(Path::new("/"));
1312 let mut builder = GitignoreBuilder::new(parent);
1313 for line in contents.lines() {
1314 builder.add_line(Some(abs_path.into()), line)?;
1315 }
1316 Ok(builder.build()?)
1317}
1318
1319impl Deref for Worktree {
1320 type Target = Snapshot;
1321
1322 fn deref(&self) -> &Self::Target {
1323 match self {
1324 Worktree::Local(worktree) => &worktree.snapshot,
1325 Worktree::Remote(worktree) => &worktree.snapshot,
1326 }
1327 }
1328}
1329
1330impl Deref for LocalWorktree {
1331 type Target = Snapshot;
1332
1333 fn deref(&self) -> &Self::Target {
1334 &self.snapshot
1335 }
1336}
1337
1338impl fmt::Debug for LocalWorktree {
1339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1340 self.snapshot.fmt(f)
1341 }
1342}
1343
1344impl RemoteWorktree {
1345 pub fn remote_id(&self) -> u64 {
1346 self.remote_id
1347 }
1348
1349 fn get_open_buffer(
1350 &mut self,
1351 path: &Path,
1352 cx: &mut ModelContext<Worktree>,
1353 ) -> Option<ModelHandle<Buffer>> {
1354 let handle = cx.handle();
1355 let mut existing_buffer = None;
1356 self.open_buffers.retain(|_buffer_id, buffer| {
1357 if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
1358 if let Some(file) = buffer.read(cx.as_ref()).file() {
1359 if file.worktree_id() == handle.id() && file.path().as_ref() == path {
1360 existing_buffer = Some(buffer);
1361 }
1362 }
1363 true
1364 } else {
1365 false
1366 }
1367 });
1368 existing_buffer
1369 }
1370
1371 fn open_buffer(
1372 &mut self,
1373 path: &Path,
1374 cx: &mut ModelContext<Worktree>,
1375 ) -> Task<Result<ModelHandle<Buffer>>> {
1376 let rpc = self.client.clone();
1377 let replica_id = self.replica_id;
1378 let project_id = self.project_id;
1379 let remote_worktree_id = self.remote_id;
1380 let root_path = self.snapshot.abs_path.clone();
1381 let path: Arc<Path> = Arc::from(path);
1382 let path_string = path.to_string_lossy().to_string();
1383 cx.spawn_weak(move |this, mut cx| async move {
1384 let entry = this
1385 .upgrade(&cx)
1386 .ok_or_else(|| anyhow!("worktree was closed"))?
1387 .read_with(&cx, |tree, _| tree.entry_for_path(&path).cloned())
1388 .ok_or_else(|| anyhow!("file does not exist"))?;
1389 let response = rpc
1390 .request(proto::OpenBuffer {
1391 project_id,
1392 worktree_id: remote_worktree_id as u64,
1393 path: path_string,
1394 })
1395 .await?;
1396
1397 let this = this
1398 .upgrade(&cx)
1399 .ok_or_else(|| anyhow!("worktree was closed"))?;
1400 let file = File {
1401 entry_id: Some(entry.id),
1402 worktree: this.clone(),
1403 worktree_path: root_path,
1404 path: entry.path,
1405 mtime: entry.mtime,
1406 is_local: false,
1407 };
1408 let language = this.read_with(&cx, |this, _| {
1409 use language::File;
1410 this.languages().select_language(file.full_path()).cloned()
1411 });
1412 let remote_buffer = response.buffer.ok_or_else(|| anyhow!("empty buffer"))?;
1413 let buffer_id = remote_buffer.id as usize;
1414 let buffer = cx.add_model(|cx| {
1415 Buffer::from_proto(replica_id, remote_buffer, Some(Box::new(file)), cx)
1416 .unwrap()
1417 .with_language(language, None, cx)
1418 });
1419 this.update(&mut cx, move |this, cx| {
1420 let this = this.as_remote_mut().unwrap();
1421 if let Some(RemoteBuffer::Operations(pending_ops)) = this
1422 .open_buffers
1423 .insert(buffer_id, RemoteBuffer::Loaded(buffer.downgrade()))
1424 {
1425 buffer.update(cx, |buf, cx| buf.apply_ops(pending_ops, cx))?;
1426 }
1427 Result::<_, anyhow::Error>::Ok(buffer)
1428 })
1429 })
1430 }
1431
1432 pub fn close_all_buffers(&mut self, cx: &mut MutableAppContext) {
1433 for (_, buffer) in self.open_buffers.drain() {
1434 if let RemoteBuffer::Loaded(buffer) = buffer {
1435 if let Some(buffer) = buffer.upgrade(cx) {
1436 buffer.update(cx, |buffer, cx| buffer.close(cx))
1437 }
1438 }
1439 }
1440 }
1441
1442 fn snapshot(&self) -> Snapshot {
1443 self.snapshot.clone()
1444 }
1445
1446 pub fn update_from_remote(
1447 &mut self,
1448 envelope: TypedEnvelope<proto::UpdateWorktree>,
1449 cx: &mut ModelContext<Worktree>,
1450 ) -> Result<()> {
1451 let mut tx = self.updates_tx.clone();
1452 let payload = envelope.payload.clone();
1453 cx.background()
1454 .spawn(async move {
1455 tx.send(payload).await.expect("receiver runs to completion");
1456 })
1457 .detach();
1458
1459 Ok(())
1460 }
1461
1462 pub fn remove_collaborator(&mut self, replica_id: ReplicaId, cx: &mut ModelContext<Worktree>) {
1463 for (_, buffer) in &self.open_buffers {
1464 if let Some(buffer) = buffer.upgrade(cx) {
1465 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
1466 }
1467 }
1468 cx.notify();
1469 }
1470}
1471
1472enum RemoteBuffer {
1473 Operations(Vec<Operation>),
1474 Loaded(WeakModelHandle<Buffer>),
1475}
1476
1477impl RemoteBuffer {
1478 fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
1479 match self {
1480 Self::Operations(_) => None,
1481 Self::Loaded(buffer) => buffer.upgrade(cx),
1482 }
1483 }
1484}
1485
1486impl Snapshot {
1487 pub fn id(&self) -> usize {
1488 self.id
1489 }
1490
1491 pub fn to_proto(&self) -> proto::Worktree {
1492 let root_name = self.root_name.clone();
1493 proto::Worktree {
1494 id: self.id as u64,
1495 root_name,
1496 entries: self
1497 .entries_by_path
1498 .cursor::<()>()
1499 .filter(|e| !e.is_ignored)
1500 .map(Into::into)
1501 .collect(),
1502 }
1503 }
1504
1505 pub fn build_update(
1506 &self,
1507 other: &Self,
1508 project_id: u64,
1509 worktree_id: u64,
1510 include_ignored: bool,
1511 ) -> proto::UpdateWorktree {
1512 let mut updated_entries = Vec::new();
1513 let mut removed_entries = Vec::new();
1514 let mut self_entries = self
1515 .entries_by_id
1516 .cursor::<()>()
1517 .filter(|e| include_ignored || !e.is_ignored)
1518 .peekable();
1519 let mut other_entries = other
1520 .entries_by_id
1521 .cursor::<()>()
1522 .filter(|e| include_ignored || !e.is_ignored)
1523 .peekable();
1524 loop {
1525 match (self_entries.peek(), other_entries.peek()) {
1526 (Some(self_entry), Some(other_entry)) => {
1527 match Ord::cmp(&self_entry.id, &other_entry.id) {
1528 Ordering::Less => {
1529 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1530 updated_entries.push(entry);
1531 self_entries.next();
1532 }
1533 Ordering::Equal => {
1534 if self_entry.scan_id != other_entry.scan_id {
1535 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1536 updated_entries.push(entry);
1537 }
1538
1539 self_entries.next();
1540 other_entries.next();
1541 }
1542 Ordering::Greater => {
1543 removed_entries.push(other_entry.id as u64);
1544 other_entries.next();
1545 }
1546 }
1547 }
1548 (Some(self_entry), None) => {
1549 let entry = self.entry_for_id(self_entry.id).unwrap().into();
1550 updated_entries.push(entry);
1551 self_entries.next();
1552 }
1553 (None, Some(other_entry)) => {
1554 removed_entries.push(other_entry.id as u64);
1555 other_entries.next();
1556 }
1557 (None, None) => break,
1558 }
1559 }
1560
1561 proto::UpdateWorktree {
1562 project_id,
1563 worktree_id,
1564 root_name: self.root_name().to_string(),
1565 updated_entries,
1566 removed_entries,
1567 }
1568 }
1569
1570 fn apply_update(&mut self, update: proto::UpdateWorktree) -> Result<()> {
1571 self.scan_id += 1;
1572 let scan_id = self.scan_id;
1573
1574 let mut entries_by_path_edits = Vec::new();
1575 let mut entries_by_id_edits = Vec::new();
1576 for entry_id in update.removed_entries {
1577 let entry_id = entry_id as usize;
1578 let entry = self
1579 .entry_for_id(entry_id)
1580 .ok_or_else(|| anyhow!("unknown entry"))?;
1581 entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
1582 entries_by_id_edits.push(Edit::Remove(entry.id));
1583 }
1584
1585 for entry in update.updated_entries {
1586 let entry = Entry::try_from((&self.root_char_bag, entry))?;
1587 if let Some(PathEntry { path, .. }) = self.entries_by_id.get(&entry.id, &()) {
1588 entries_by_path_edits.push(Edit::Remove(PathKey(path.clone())));
1589 }
1590 entries_by_id_edits.push(Edit::Insert(PathEntry {
1591 id: entry.id,
1592 path: entry.path.clone(),
1593 is_ignored: entry.is_ignored,
1594 scan_id,
1595 }));
1596 entries_by_path_edits.push(Edit::Insert(entry));
1597 }
1598
1599 self.entries_by_path.edit(entries_by_path_edits, &());
1600 self.entries_by_id.edit(entries_by_id_edits, &());
1601
1602 Ok(())
1603 }
1604
1605 pub fn file_count(&self) -> usize {
1606 self.entries_by_path.summary().file_count
1607 }
1608
1609 pub fn visible_file_count(&self) -> usize {
1610 self.entries_by_path.summary().visible_file_count
1611 }
1612
1613 fn traverse_from_offset(
1614 &self,
1615 include_dirs: bool,
1616 include_ignored: bool,
1617 start_offset: usize,
1618 ) -> Traversal {
1619 let mut cursor = self.entries_by_path.cursor();
1620 cursor.seek(
1621 &TraversalTarget::Count {
1622 count: start_offset,
1623 include_dirs,
1624 include_ignored,
1625 },
1626 Bias::Right,
1627 &(),
1628 );
1629 Traversal {
1630 cursor,
1631 include_dirs,
1632 include_ignored,
1633 }
1634 }
1635
1636 fn traverse_from_path(
1637 &self,
1638 include_dirs: bool,
1639 include_ignored: bool,
1640 path: &Path,
1641 ) -> Traversal {
1642 let mut cursor = self.entries_by_path.cursor();
1643 cursor.seek(&TraversalTarget::Path(path), Bias::Left, &());
1644 Traversal {
1645 cursor,
1646 include_dirs,
1647 include_ignored,
1648 }
1649 }
1650
1651 pub fn files(&self, include_ignored: bool, start: usize) -> Traversal {
1652 self.traverse_from_offset(false, include_ignored, start)
1653 }
1654
1655 pub fn entries(&self, include_ignored: bool) -> Traversal {
1656 self.traverse_from_offset(true, include_ignored, 0)
1657 }
1658
1659 pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
1660 let empty_path = Path::new("");
1661 self.entries_by_path
1662 .cursor::<()>()
1663 .filter(move |entry| entry.path.as_ref() != empty_path)
1664 .map(|entry| &entry.path)
1665 }
1666
1667 fn child_entries<'a>(&'a self, parent_path: &'a Path) -> ChildEntriesIter<'a> {
1668 let mut cursor = self.entries_by_path.cursor();
1669 cursor.seek(&TraversalTarget::Path(parent_path), Bias::Right, &());
1670 let traversal = Traversal {
1671 cursor,
1672 include_dirs: true,
1673 include_ignored: true,
1674 };
1675 ChildEntriesIter {
1676 traversal,
1677 parent_path,
1678 }
1679 }
1680
1681 pub fn root_entry(&self) -> Option<&Entry> {
1682 self.entry_for_path("")
1683 }
1684
1685 pub fn root_name(&self) -> &str {
1686 &self.root_name
1687 }
1688
1689 pub fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
1690 let path = path.as_ref();
1691 self.traverse_from_path(true, true, path)
1692 .entry()
1693 .and_then(|entry| {
1694 if entry.path.as_ref() == path {
1695 Some(entry)
1696 } else {
1697 None
1698 }
1699 })
1700 }
1701
1702 pub fn entry_for_id(&self, id: usize) -> Option<&Entry> {
1703 let entry = self.entries_by_id.get(&id, &())?;
1704 self.entry_for_path(&entry.path)
1705 }
1706
1707 pub fn inode_for_path(&self, path: impl AsRef<Path>) -> Option<u64> {
1708 self.entry_for_path(path.as_ref()).map(|e| e.inode)
1709 }
1710
1711 fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry {
1712 if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) {
1713 let abs_path = self.abs_path.join(&entry.path);
1714 match build_gitignore(&abs_path, fs) {
1715 Ok(ignore) => {
1716 let ignore_dir_path = entry.path.parent().unwrap();
1717 self.ignores
1718 .insert(ignore_dir_path.into(), (Arc::new(ignore), self.scan_id));
1719 }
1720 Err(error) => {
1721 log::error!(
1722 "error loading .gitignore file {:?} - {:?}",
1723 &entry.path,
1724 error
1725 );
1726 }
1727 }
1728 }
1729
1730 self.reuse_entry_id(&mut entry);
1731 self.entries_by_path.insert_or_replace(entry.clone(), &());
1732 self.entries_by_id.insert_or_replace(
1733 PathEntry {
1734 id: entry.id,
1735 path: entry.path.clone(),
1736 is_ignored: entry.is_ignored,
1737 scan_id: self.scan_id,
1738 },
1739 &(),
1740 );
1741 entry
1742 }
1743
1744 fn populate_dir(
1745 &mut self,
1746 parent_path: Arc<Path>,
1747 entries: impl IntoIterator<Item = Entry>,
1748 ignore: Option<Arc<Gitignore>>,
1749 ) {
1750 let mut parent_entry = self
1751 .entries_by_path
1752 .get(&PathKey(parent_path.clone()), &())
1753 .unwrap()
1754 .clone();
1755 if let Some(ignore) = ignore {
1756 self.ignores.insert(parent_path, (ignore, self.scan_id));
1757 }
1758 if matches!(parent_entry.kind, EntryKind::PendingDir) {
1759 parent_entry.kind = EntryKind::Dir;
1760 } else {
1761 unreachable!();
1762 }
1763
1764 let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)];
1765 let mut entries_by_id_edits = Vec::new();
1766
1767 for mut entry in entries {
1768 self.reuse_entry_id(&mut entry);
1769 entries_by_id_edits.push(Edit::Insert(PathEntry {
1770 id: entry.id,
1771 path: entry.path.clone(),
1772 is_ignored: entry.is_ignored,
1773 scan_id: self.scan_id,
1774 }));
1775 entries_by_path_edits.push(Edit::Insert(entry));
1776 }
1777
1778 self.entries_by_path.edit(entries_by_path_edits, &());
1779 self.entries_by_id.edit(entries_by_id_edits, &());
1780 }
1781
1782 fn reuse_entry_id(&mut self, entry: &mut Entry) {
1783 if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) {
1784 entry.id = removed_entry_id;
1785 } else if let Some(existing_entry) = self.entry_for_path(&entry.path) {
1786 entry.id = existing_entry.id;
1787 }
1788 }
1789
1790 fn remove_path(&mut self, path: &Path) {
1791 let mut new_entries;
1792 let removed_entries;
1793 {
1794 let mut cursor = self.entries_by_path.cursor::<TraversalProgress>();
1795 new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
1796 removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
1797 new_entries.push_tree(cursor.suffix(&()), &());
1798 }
1799 self.entries_by_path = new_entries;
1800
1801 let mut entries_by_id_edits = Vec::new();
1802 for entry in removed_entries.cursor::<()>() {
1803 let removed_entry_id = self
1804 .removed_entry_ids
1805 .entry(entry.inode)
1806 .or_insert(entry.id);
1807 *removed_entry_id = cmp::max(*removed_entry_id, entry.id);
1808 entries_by_id_edits.push(Edit::Remove(entry.id));
1809 }
1810 self.entries_by_id.edit(entries_by_id_edits, &());
1811
1812 if path.file_name() == Some(&GITIGNORE) {
1813 if let Some((_, scan_id)) = self.ignores.get_mut(path.parent().unwrap()) {
1814 *scan_id = self.scan_id;
1815 }
1816 }
1817 }
1818
1819 fn ignore_stack_for_path(&self, path: &Path, is_dir: bool) -> Arc<IgnoreStack> {
1820 let mut new_ignores = Vec::new();
1821 for ancestor in path.ancestors().skip(1) {
1822 if let Some((ignore, _)) = self.ignores.get(ancestor) {
1823 new_ignores.push((ancestor, Some(ignore.clone())));
1824 } else {
1825 new_ignores.push((ancestor, None));
1826 }
1827 }
1828
1829 let mut ignore_stack = IgnoreStack::none();
1830 for (parent_path, ignore) in new_ignores.into_iter().rev() {
1831 if ignore_stack.is_path_ignored(&parent_path, true) {
1832 ignore_stack = IgnoreStack::all();
1833 break;
1834 } else if let Some(ignore) = ignore {
1835 ignore_stack = ignore_stack.append(Arc::from(parent_path), ignore);
1836 }
1837 }
1838
1839 if ignore_stack.is_path_ignored(path, is_dir) {
1840 ignore_stack = IgnoreStack::all();
1841 }
1842
1843 ignore_stack
1844 }
1845}
1846
1847impl fmt::Debug for Snapshot {
1848 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1849 for entry in self.entries_by_path.cursor::<()>() {
1850 for _ in entry.path.ancestors().skip(1) {
1851 write!(f, " ")?;
1852 }
1853 writeln!(f, "{:?} (inode: {})", entry.path, entry.inode)?;
1854 }
1855 Ok(())
1856 }
1857}
1858
1859#[derive(Clone, PartialEq)]
1860pub struct File {
1861 entry_id: Option<usize>,
1862 worktree: ModelHandle<Worktree>,
1863 worktree_path: Arc<Path>,
1864 pub path: Arc<Path>,
1865 pub mtime: SystemTime,
1866 is_local: bool,
1867}
1868
1869impl language::File for File {
1870 fn worktree_id(&self) -> usize {
1871 self.worktree.id()
1872 }
1873
1874 fn entry_id(&self) -> Option<usize> {
1875 self.entry_id
1876 }
1877
1878 fn mtime(&self) -> SystemTime {
1879 self.mtime
1880 }
1881
1882 fn path(&self) -> &Arc<Path> {
1883 &self.path
1884 }
1885
1886 fn abs_path(&self) -> Option<PathBuf> {
1887 if self.is_local {
1888 Some(self.worktree_path.join(&self.path))
1889 } else {
1890 None
1891 }
1892 }
1893
1894 fn full_path(&self) -> PathBuf {
1895 let mut full_path = PathBuf::new();
1896 if let Some(worktree_name) = self.worktree_path.file_name() {
1897 full_path.push(worktree_name);
1898 }
1899 full_path.push(&self.path);
1900 full_path
1901 }
1902
1903 /// Returns the last component of this handle's absolute path. If this handle refers to the root
1904 /// of its worktree, then this method will return the name of the worktree itself.
1905 fn file_name<'a>(&'a self) -> Option<OsString> {
1906 self.path
1907 .file_name()
1908 .or_else(|| self.worktree_path.file_name())
1909 .map(Into::into)
1910 }
1911
1912 fn is_deleted(&self) -> bool {
1913 self.entry_id.is_none()
1914 }
1915
1916 fn save(
1917 &self,
1918 buffer_id: u64,
1919 text: Rope,
1920 version: clock::Global,
1921 cx: &mut MutableAppContext,
1922 ) -> Task<Result<(clock::Global, SystemTime)>> {
1923 let worktree_id = self.worktree.read(cx).id() as u64;
1924 self.worktree.update(cx, |worktree, cx| match worktree {
1925 Worktree::Local(worktree) => {
1926 let rpc = worktree.client.clone();
1927 let project_id = worktree.share.as_ref().map(|share| share.project_id);
1928 let save = worktree.save(self.path.clone(), text, cx);
1929 cx.background().spawn(async move {
1930 let entry = save.await?;
1931 if let Some(project_id) = project_id {
1932 rpc.send(proto::BufferSaved {
1933 project_id,
1934 worktree_id,
1935 buffer_id,
1936 version: (&version).into(),
1937 mtime: Some(entry.mtime.into()),
1938 })
1939 .await?;
1940 }
1941 Ok((version, entry.mtime))
1942 })
1943 }
1944 Worktree::Remote(worktree) => {
1945 let rpc = worktree.client.clone();
1946 let project_id = worktree.project_id;
1947 cx.foreground().spawn(async move {
1948 let response = rpc
1949 .request(proto::SaveBuffer {
1950 project_id,
1951 worktree_id,
1952 buffer_id,
1953 })
1954 .await?;
1955 let version = response.version.try_into()?;
1956 let mtime = response
1957 .mtime
1958 .ok_or_else(|| anyhow!("missing mtime"))?
1959 .into();
1960 Ok((version, mtime))
1961 })
1962 }
1963 })
1964 }
1965
1966 fn load_local(&self, cx: &AppContext) -> Option<Task<Result<String>>> {
1967 let worktree = self.worktree.read(cx).as_local()?;
1968 let abs_path = worktree.absolutize(&self.path);
1969 let fs = worktree.fs.clone();
1970 Some(
1971 cx.background()
1972 .spawn(async move { fs.load(&abs_path).await }),
1973 )
1974 }
1975
1976 fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext) {
1977 self.worktree.update(cx, |worktree, cx| {
1978 worktree.send_buffer_update(buffer_id, operation, cx);
1979 });
1980 }
1981
1982 fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext) {
1983 self.worktree.update(cx, |worktree, cx| {
1984 if let Worktree::Remote(worktree) = worktree {
1985 let project_id = worktree.project_id;
1986 let worktree_id = worktree.remote_id;
1987 let rpc = worktree.client.clone();
1988 cx.background()
1989 .spawn(async move {
1990 if let Err(error) = rpc
1991 .send(proto::CloseBuffer {
1992 project_id,
1993 worktree_id,
1994 buffer_id,
1995 })
1996 .await
1997 {
1998 log::error!("error closing remote buffer: {}", error);
1999 }
2000 })
2001 .detach();
2002 }
2003 });
2004 }
2005
2006 fn boxed_clone(&self) -> Box<dyn language::File> {
2007 Box::new(self.clone())
2008 }
2009
2010 fn as_any(&self) -> &dyn Any {
2011 self
2012 }
2013}
2014
2015#[derive(Clone, Debug)]
2016pub struct Entry {
2017 pub id: usize,
2018 pub kind: EntryKind,
2019 pub path: Arc<Path>,
2020 pub inode: u64,
2021 pub mtime: SystemTime,
2022 pub is_symlink: bool,
2023 pub is_ignored: bool,
2024}
2025
2026#[derive(Clone, Debug)]
2027pub enum EntryKind {
2028 PendingDir,
2029 Dir,
2030 File(CharBag),
2031}
2032
2033impl Entry {
2034 fn new(
2035 path: Arc<Path>,
2036 metadata: &fs::Metadata,
2037 next_entry_id: &AtomicUsize,
2038 root_char_bag: CharBag,
2039 ) -> Self {
2040 Self {
2041 id: next_entry_id.fetch_add(1, SeqCst),
2042 kind: if metadata.is_dir {
2043 EntryKind::PendingDir
2044 } else {
2045 EntryKind::File(char_bag_for_path(root_char_bag, &path))
2046 },
2047 path,
2048 inode: metadata.inode,
2049 mtime: metadata.mtime,
2050 is_symlink: metadata.is_symlink,
2051 is_ignored: false,
2052 }
2053 }
2054
2055 pub fn is_dir(&self) -> bool {
2056 matches!(self.kind, EntryKind::Dir | EntryKind::PendingDir)
2057 }
2058
2059 pub fn is_file(&self) -> bool {
2060 matches!(self.kind, EntryKind::File(_))
2061 }
2062}
2063
2064impl sum_tree::Item for Entry {
2065 type Summary = EntrySummary;
2066
2067 fn summary(&self) -> Self::Summary {
2068 let visible_count = if self.is_ignored { 0 } else { 1 };
2069 let file_count;
2070 let visible_file_count;
2071 if self.is_file() {
2072 file_count = 1;
2073 visible_file_count = visible_count;
2074 } else {
2075 file_count = 0;
2076 visible_file_count = 0;
2077 }
2078
2079 EntrySummary {
2080 max_path: self.path.clone(),
2081 count: 1,
2082 visible_count,
2083 file_count,
2084 visible_file_count,
2085 }
2086 }
2087}
2088
2089impl sum_tree::KeyedItem for Entry {
2090 type Key = PathKey;
2091
2092 fn key(&self) -> Self::Key {
2093 PathKey(self.path.clone())
2094 }
2095}
2096
2097#[derive(Clone, Debug)]
2098pub struct EntrySummary {
2099 max_path: Arc<Path>,
2100 count: usize,
2101 visible_count: usize,
2102 file_count: usize,
2103 visible_file_count: usize,
2104}
2105
2106impl Default for EntrySummary {
2107 fn default() -> Self {
2108 Self {
2109 max_path: Arc::from(Path::new("")),
2110 count: 0,
2111 visible_count: 0,
2112 file_count: 0,
2113 visible_file_count: 0,
2114 }
2115 }
2116}
2117
2118impl sum_tree::Summary for EntrySummary {
2119 type Context = ();
2120
2121 fn add_summary(&mut self, rhs: &Self, _: &()) {
2122 self.max_path = rhs.max_path.clone();
2123 self.visible_count += rhs.visible_count;
2124 self.file_count += rhs.file_count;
2125 self.visible_file_count += rhs.visible_file_count;
2126 }
2127}
2128
2129#[derive(Clone, Debug)]
2130struct PathEntry {
2131 id: usize,
2132 path: Arc<Path>,
2133 is_ignored: bool,
2134 scan_id: usize,
2135}
2136
2137impl sum_tree::Item for PathEntry {
2138 type Summary = PathEntrySummary;
2139
2140 fn summary(&self) -> Self::Summary {
2141 PathEntrySummary { max_id: self.id }
2142 }
2143}
2144
2145impl sum_tree::KeyedItem for PathEntry {
2146 type Key = usize;
2147
2148 fn key(&self) -> Self::Key {
2149 self.id
2150 }
2151}
2152
2153#[derive(Clone, Debug, Default)]
2154struct PathEntrySummary {
2155 max_id: usize,
2156}
2157
2158impl sum_tree::Summary for PathEntrySummary {
2159 type Context = ();
2160
2161 fn add_summary(&mut self, summary: &Self, _: &Self::Context) {
2162 self.max_id = summary.max_id;
2163 }
2164}
2165
2166impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for usize {
2167 fn add_summary(&mut self, summary: &'a PathEntrySummary, _: &()) {
2168 *self = summary.max_id;
2169 }
2170}
2171
2172#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
2173pub struct PathKey(Arc<Path>);
2174
2175impl Default for PathKey {
2176 fn default() -> Self {
2177 Self(Path::new("").into())
2178 }
2179}
2180
2181impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
2182 fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
2183 self.0 = summary.max_path.clone();
2184 }
2185}
2186
2187struct BackgroundScanner {
2188 fs: Arc<dyn Fs>,
2189 snapshot: Arc<Mutex<Snapshot>>,
2190 notify: Sender<ScanState>,
2191 executor: Arc<executor::Background>,
2192}
2193
2194impl BackgroundScanner {
2195 fn new(
2196 snapshot: Arc<Mutex<Snapshot>>,
2197 notify: Sender<ScanState>,
2198 fs: Arc<dyn Fs>,
2199 executor: Arc<executor::Background>,
2200 ) -> Self {
2201 Self {
2202 fs,
2203 snapshot,
2204 notify,
2205 executor,
2206 }
2207 }
2208
2209 fn abs_path(&self) -> Arc<Path> {
2210 self.snapshot.lock().abs_path.clone()
2211 }
2212
2213 fn snapshot(&self) -> Snapshot {
2214 self.snapshot.lock().clone()
2215 }
2216
2217 async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
2218 if self.notify.send(ScanState::Scanning).await.is_err() {
2219 return;
2220 }
2221
2222 if let Err(err) = self.scan_dirs().await {
2223 if self
2224 .notify
2225 .send(ScanState::Err(Arc::new(err)))
2226 .await
2227 .is_err()
2228 {
2229 return;
2230 }
2231 }
2232
2233 if self.notify.send(ScanState::Idle).await.is_err() {
2234 return;
2235 }
2236
2237 futures::pin_mut!(events_rx);
2238 while let Some(events) = events_rx.next().await {
2239 if self.notify.send(ScanState::Scanning).await.is_err() {
2240 break;
2241 }
2242
2243 if !self.process_events(events).await {
2244 break;
2245 }
2246
2247 if self.notify.send(ScanState::Idle).await.is_err() {
2248 break;
2249 }
2250 }
2251 }
2252
2253 async fn scan_dirs(&mut self) -> Result<()> {
2254 let root_char_bag;
2255 let next_entry_id;
2256 let is_dir;
2257 {
2258 let snapshot = self.snapshot.lock();
2259 root_char_bag = snapshot.root_char_bag;
2260 next_entry_id = snapshot.next_entry_id.clone();
2261 is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir())
2262 };
2263
2264 if is_dir {
2265 let path: Arc<Path> = Arc::from(Path::new(""));
2266 let abs_path = self.abs_path();
2267 let (tx, rx) = channel::unbounded();
2268 tx.send(ScanJob {
2269 abs_path: abs_path.to_path_buf(),
2270 path,
2271 ignore_stack: IgnoreStack::none(),
2272 scan_queue: tx.clone(),
2273 })
2274 .await
2275 .unwrap();
2276 drop(tx);
2277
2278 self.executor
2279 .scoped(|scope| {
2280 for _ in 0..self.executor.num_cpus() {
2281 scope.spawn(async {
2282 while let Ok(job) = rx.recv().await {
2283 if let Err(err) = self
2284 .scan_dir(root_char_bag, next_entry_id.clone(), &job)
2285 .await
2286 {
2287 log::error!("error scanning {:?}: {}", job.abs_path, err);
2288 }
2289 }
2290 });
2291 }
2292 })
2293 .await;
2294 }
2295
2296 Ok(())
2297 }
2298
2299 async fn scan_dir(
2300 &self,
2301 root_char_bag: CharBag,
2302 next_entry_id: Arc<AtomicUsize>,
2303 job: &ScanJob,
2304 ) -> Result<()> {
2305 let mut new_entries: Vec<Entry> = Vec::new();
2306 let mut new_jobs: Vec<ScanJob> = Vec::new();
2307 let mut ignore_stack = job.ignore_stack.clone();
2308 let mut new_ignore = None;
2309
2310 let mut child_paths = self.fs.read_dir(&job.abs_path).await?;
2311 while let Some(child_abs_path) = child_paths.next().await {
2312 let child_abs_path = match child_abs_path {
2313 Ok(child_abs_path) => child_abs_path,
2314 Err(error) => {
2315 log::error!("error processing entry {:?}", error);
2316 continue;
2317 }
2318 };
2319 let child_name = child_abs_path.file_name().unwrap();
2320 let child_path: Arc<Path> = job.path.join(child_name).into();
2321 let child_metadata = match self.fs.metadata(&child_abs_path).await? {
2322 Some(metadata) => metadata,
2323 None => continue,
2324 };
2325
2326 // If we find a .gitignore, add it to the stack of ignores used to determine which paths are ignored
2327 if child_name == *GITIGNORE {
2328 match build_gitignore(&child_abs_path, self.fs.as_ref()) {
2329 Ok(ignore) => {
2330 let ignore = Arc::new(ignore);
2331 ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone());
2332 new_ignore = Some(ignore);
2333 }
2334 Err(error) => {
2335 log::error!(
2336 "error loading .gitignore file {:?} - {:?}",
2337 child_name,
2338 error
2339 );
2340 }
2341 }
2342
2343 // Update ignore status of any child entries we've already processed to reflect the
2344 // ignore file in the current directory. Because `.gitignore` starts with a `.`,
2345 // there should rarely be too numerous. Update the ignore stack associated with any
2346 // new jobs as well.
2347 let mut new_jobs = new_jobs.iter_mut();
2348 for entry in &mut new_entries {
2349 entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir());
2350 if entry.is_dir() {
2351 new_jobs.next().unwrap().ignore_stack = if entry.is_ignored {
2352 IgnoreStack::all()
2353 } else {
2354 ignore_stack.clone()
2355 };
2356 }
2357 }
2358 }
2359
2360 let mut child_entry = Entry::new(
2361 child_path.clone(),
2362 &child_metadata,
2363 &next_entry_id,
2364 root_char_bag,
2365 );
2366
2367 if child_metadata.is_dir {
2368 let is_ignored = ignore_stack.is_path_ignored(&child_path, true);
2369 child_entry.is_ignored = is_ignored;
2370 new_entries.push(child_entry);
2371 new_jobs.push(ScanJob {
2372 abs_path: child_abs_path,
2373 path: child_path,
2374 ignore_stack: if is_ignored {
2375 IgnoreStack::all()
2376 } else {
2377 ignore_stack.clone()
2378 },
2379 scan_queue: job.scan_queue.clone(),
2380 });
2381 } else {
2382 child_entry.is_ignored = ignore_stack.is_path_ignored(&child_path, false);
2383 new_entries.push(child_entry);
2384 };
2385 }
2386
2387 self.snapshot
2388 .lock()
2389 .populate_dir(job.path.clone(), new_entries, new_ignore);
2390 for new_job in new_jobs {
2391 job.scan_queue.send(new_job).await.unwrap();
2392 }
2393
2394 Ok(())
2395 }
2396
2397 async fn process_events(&mut self, mut events: Vec<fsevent::Event>) -> bool {
2398 let mut snapshot = self.snapshot();
2399 snapshot.scan_id += 1;
2400
2401 let root_abs_path = if let Ok(abs_path) = self.fs.canonicalize(&snapshot.abs_path).await {
2402 abs_path
2403 } else {
2404 return false;
2405 };
2406 let root_char_bag = snapshot.root_char_bag;
2407 let next_entry_id = snapshot.next_entry_id.clone();
2408
2409 events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
2410 events.dedup_by(|a, b| a.path.starts_with(&b.path));
2411
2412 for event in &events {
2413 match event.path.strip_prefix(&root_abs_path) {
2414 Ok(path) => snapshot.remove_path(&path),
2415 Err(_) => {
2416 log::error!(
2417 "unexpected event {:?} for root path {:?}",
2418 event.path,
2419 root_abs_path
2420 );
2421 continue;
2422 }
2423 }
2424 }
2425
2426 let (scan_queue_tx, scan_queue_rx) = channel::unbounded();
2427 for event in events {
2428 let path: Arc<Path> = match event.path.strip_prefix(&root_abs_path) {
2429 Ok(path) => Arc::from(path.to_path_buf()),
2430 Err(_) => {
2431 log::error!(
2432 "unexpected event {:?} for root path {:?}",
2433 event.path,
2434 root_abs_path
2435 );
2436 continue;
2437 }
2438 };
2439
2440 match self.fs.metadata(&event.path).await {
2441 Ok(Some(metadata)) => {
2442 let ignore_stack = snapshot.ignore_stack_for_path(&path, metadata.is_dir);
2443 let mut fs_entry = Entry::new(
2444 path.clone(),
2445 &metadata,
2446 snapshot.next_entry_id.as_ref(),
2447 snapshot.root_char_bag,
2448 );
2449 fs_entry.is_ignored = ignore_stack.is_all();
2450 snapshot.insert_entry(fs_entry, self.fs.as_ref());
2451 if metadata.is_dir {
2452 scan_queue_tx
2453 .send(ScanJob {
2454 abs_path: event.path,
2455 path,
2456 ignore_stack,
2457 scan_queue: scan_queue_tx.clone(),
2458 })
2459 .await
2460 .unwrap();
2461 }
2462 }
2463 Ok(None) => {}
2464 Err(err) => {
2465 // TODO - create a special 'error' entry in the entries tree to mark this
2466 log::error!("error reading file on event {:?}", err);
2467 }
2468 }
2469 }
2470
2471 *self.snapshot.lock() = snapshot;
2472
2473 // Scan any directories that were created as part of this event batch.
2474 drop(scan_queue_tx);
2475 self.executor
2476 .scoped(|scope| {
2477 for _ in 0..self.executor.num_cpus() {
2478 scope.spawn(async {
2479 while let Ok(job) = scan_queue_rx.recv().await {
2480 if let Err(err) = self
2481 .scan_dir(root_char_bag, next_entry_id.clone(), &job)
2482 .await
2483 {
2484 log::error!("error scanning {:?}: {}", job.abs_path, err);
2485 }
2486 }
2487 });
2488 }
2489 })
2490 .await;
2491
2492 // Attempt to detect renames only over a single batch of file-system events.
2493 self.snapshot.lock().removed_entry_ids.clear();
2494
2495 self.update_ignore_statuses().await;
2496 true
2497 }
2498
2499 async fn update_ignore_statuses(&self) {
2500 let mut snapshot = self.snapshot();
2501
2502 let mut ignores_to_update = Vec::new();
2503 let mut ignores_to_delete = Vec::new();
2504 for (parent_path, (_, scan_id)) in &snapshot.ignores {
2505 if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() {
2506 ignores_to_update.push(parent_path.clone());
2507 }
2508
2509 let ignore_path = parent_path.join(&*GITIGNORE);
2510 if snapshot.entry_for_path(ignore_path).is_none() {
2511 ignores_to_delete.push(parent_path.clone());
2512 }
2513 }
2514
2515 for parent_path in ignores_to_delete {
2516 snapshot.ignores.remove(&parent_path);
2517 self.snapshot.lock().ignores.remove(&parent_path);
2518 }
2519
2520 let (ignore_queue_tx, ignore_queue_rx) = channel::unbounded();
2521 ignores_to_update.sort_unstable();
2522 let mut ignores_to_update = ignores_to_update.into_iter().peekable();
2523 while let Some(parent_path) = ignores_to_update.next() {
2524 while ignores_to_update
2525 .peek()
2526 .map_or(false, |p| p.starts_with(&parent_path))
2527 {
2528 ignores_to_update.next().unwrap();
2529 }
2530
2531 let ignore_stack = snapshot.ignore_stack_for_path(&parent_path, true);
2532 ignore_queue_tx
2533 .send(UpdateIgnoreStatusJob {
2534 path: parent_path,
2535 ignore_stack,
2536 ignore_queue: ignore_queue_tx.clone(),
2537 })
2538 .await
2539 .unwrap();
2540 }
2541 drop(ignore_queue_tx);
2542
2543 self.executor
2544 .scoped(|scope| {
2545 for _ in 0..self.executor.num_cpus() {
2546 scope.spawn(async {
2547 while let Ok(job) = ignore_queue_rx.recv().await {
2548 self.update_ignore_status(job, &snapshot).await;
2549 }
2550 });
2551 }
2552 })
2553 .await;
2554 }
2555
2556 async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &Snapshot) {
2557 let mut ignore_stack = job.ignore_stack;
2558 if let Some((ignore, _)) = snapshot.ignores.get(&job.path) {
2559 ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone());
2560 }
2561
2562 let mut entries_by_id_edits = Vec::new();
2563 let mut entries_by_path_edits = Vec::new();
2564 for mut entry in snapshot.child_entries(&job.path).cloned() {
2565 let was_ignored = entry.is_ignored;
2566 entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir());
2567 if entry.is_dir() {
2568 let child_ignore_stack = if entry.is_ignored {
2569 IgnoreStack::all()
2570 } else {
2571 ignore_stack.clone()
2572 };
2573 job.ignore_queue
2574 .send(UpdateIgnoreStatusJob {
2575 path: entry.path.clone(),
2576 ignore_stack: child_ignore_stack,
2577 ignore_queue: job.ignore_queue.clone(),
2578 })
2579 .await
2580 .unwrap();
2581 }
2582
2583 if entry.is_ignored != was_ignored {
2584 let mut path_entry = snapshot.entries_by_id.get(&entry.id, &()).unwrap().clone();
2585 path_entry.scan_id = snapshot.scan_id;
2586 path_entry.is_ignored = entry.is_ignored;
2587 entries_by_id_edits.push(Edit::Insert(path_entry));
2588 entries_by_path_edits.push(Edit::Insert(entry));
2589 }
2590 }
2591
2592 let mut snapshot = self.snapshot.lock();
2593 snapshot.entries_by_path.edit(entries_by_path_edits, &());
2594 snapshot.entries_by_id.edit(entries_by_id_edits, &());
2595 }
2596}
2597
2598async fn refresh_entry(
2599 fs: &dyn Fs,
2600 snapshot: &Mutex<Snapshot>,
2601 path: Arc<Path>,
2602 abs_path: &Path,
2603) -> Result<Entry> {
2604 let root_char_bag;
2605 let next_entry_id;
2606 {
2607 let snapshot = snapshot.lock();
2608 root_char_bag = snapshot.root_char_bag;
2609 next_entry_id = snapshot.next_entry_id.clone();
2610 }
2611 let entry = Entry::new(
2612 path,
2613 &fs.metadata(abs_path)
2614 .await?
2615 .ok_or_else(|| anyhow!("could not read saved file metadata"))?,
2616 &next_entry_id,
2617 root_char_bag,
2618 );
2619 Ok(snapshot.lock().insert_entry(entry, fs))
2620}
2621
2622fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag {
2623 let mut result = root_char_bag;
2624 result.extend(
2625 path.to_string_lossy()
2626 .chars()
2627 .map(|c| c.to_ascii_lowercase()),
2628 );
2629 result
2630}
2631
2632struct ScanJob {
2633 abs_path: PathBuf,
2634 path: Arc<Path>,
2635 ignore_stack: Arc<IgnoreStack>,
2636 scan_queue: Sender<ScanJob>,
2637}
2638
2639struct UpdateIgnoreStatusJob {
2640 path: Arc<Path>,
2641 ignore_stack: Arc<IgnoreStack>,
2642 ignore_queue: Sender<UpdateIgnoreStatusJob>,
2643}
2644
2645pub trait WorktreeHandle {
2646 #[cfg(test)]
2647 fn flush_fs_events<'a>(
2648 &self,
2649 cx: &'a gpui::TestAppContext,
2650 ) -> futures::future::LocalBoxFuture<'a, ()>;
2651}
2652
2653impl WorktreeHandle for ModelHandle<Worktree> {
2654 // When the worktree's FS event stream sometimes delivers "redundant" events for FS changes that
2655 // occurred before the worktree was constructed. These events can cause the worktree to perfrom
2656 // extra directory scans, and emit extra scan-state notifications.
2657 //
2658 // This function mutates the worktree's directory and waits for those mutations to be picked up,
2659 // to ensure that all redundant FS events have already been processed.
2660 #[cfg(test)]
2661 fn flush_fs_events<'a>(
2662 &self,
2663 cx: &'a gpui::TestAppContext,
2664 ) -> futures::future::LocalBoxFuture<'a, ()> {
2665 use smol::future::FutureExt;
2666
2667 let filename = "fs-event-sentinel";
2668 let root_path = cx.read(|cx| self.read(cx).abs_path.clone());
2669 let tree = self.clone();
2670 async move {
2671 std::fs::write(root_path.join(filename), "").unwrap();
2672 tree.condition(&cx, |tree, _| tree.entry_for_path(filename).is_some())
2673 .await;
2674
2675 std::fs::remove_file(root_path.join(filename)).unwrap();
2676 tree.condition(&cx, |tree, _| tree.entry_for_path(filename).is_none())
2677 .await;
2678
2679 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2680 .await;
2681 }
2682 .boxed_local()
2683 }
2684}
2685
2686#[derive(Clone, Debug)]
2687struct TraversalProgress<'a> {
2688 max_path: &'a Path,
2689 count: usize,
2690 visible_count: usize,
2691 file_count: usize,
2692 visible_file_count: usize,
2693}
2694
2695impl<'a> TraversalProgress<'a> {
2696 fn count(&self, include_dirs: bool, include_ignored: bool) -> usize {
2697 match (include_ignored, include_dirs) {
2698 (true, true) => self.count,
2699 (true, false) => self.file_count,
2700 (false, true) => self.visible_count,
2701 (false, false) => self.visible_file_count,
2702 }
2703 }
2704}
2705
2706impl<'a> sum_tree::Dimension<'a, EntrySummary> for TraversalProgress<'a> {
2707 fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) {
2708 self.max_path = summary.max_path.as_ref();
2709 self.count += summary.count;
2710 self.visible_count += summary.visible_count;
2711 self.file_count += summary.file_count;
2712 self.visible_file_count += summary.visible_file_count;
2713 }
2714}
2715
2716impl<'a> Default for TraversalProgress<'a> {
2717 fn default() -> Self {
2718 Self {
2719 max_path: Path::new(""),
2720 count: 0,
2721 visible_count: 0,
2722 file_count: 0,
2723 visible_file_count: 0,
2724 }
2725 }
2726}
2727
2728pub struct Traversal<'a> {
2729 cursor: sum_tree::Cursor<'a, Entry, TraversalProgress<'a>>,
2730 include_ignored: bool,
2731 include_dirs: bool,
2732}
2733
2734impl<'a> Traversal<'a> {
2735 pub fn advance(&mut self) -> bool {
2736 self.advance_to_offset(self.offset() + 1)
2737 }
2738
2739 pub fn advance_to_offset(&mut self, offset: usize) -> bool {
2740 self.cursor.seek_forward(
2741 &TraversalTarget::Count {
2742 count: offset,
2743 include_dirs: self.include_dirs,
2744 include_ignored: self.include_ignored,
2745 },
2746 Bias::Right,
2747 &(),
2748 )
2749 }
2750
2751 pub fn advance_to_sibling(&mut self) -> bool {
2752 while let Some(entry) = self.cursor.item() {
2753 self.cursor.seek_forward(
2754 &TraversalTarget::PathSuccessor(&entry.path),
2755 Bias::Left,
2756 &(),
2757 );
2758 if let Some(entry) = self.cursor.item() {
2759 if (self.include_dirs || !entry.is_dir())
2760 && (self.include_ignored || !entry.is_ignored)
2761 {
2762 return true;
2763 }
2764 }
2765 }
2766 false
2767 }
2768
2769 pub fn entry(&self) -> Option<&'a Entry> {
2770 self.cursor.item()
2771 }
2772
2773 pub fn offset(&self) -> usize {
2774 self.cursor
2775 .start()
2776 .count(self.include_dirs, self.include_ignored)
2777 }
2778}
2779
2780impl<'a> Iterator for Traversal<'a> {
2781 type Item = &'a Entry;
2782
2783 fn next(&mut self) -> Option<Self::Item> {
2784 if let Some(item) = self.entry() {
2785 self.advance();
2786 Some(item)
2787 } else {
2788 None
2789 }
2790 }
2791}
2792
2793#[derive(Debug)]
2794enum TraversalTarget<'a> {
2795 Path(&'a Path),
2796 PathSuccessor(&'a Path),
2797 Count {
2798 count: usize,
2799 include_ignored: bool,
2800 include_dirs: bool,
2801 },
2802}
2803
2804impl<'a, 'b> SeekTarget<'a, EntrySummary, TraversalProgress<'a>> for TraversalTarget<'b> {
2805 fn cmp(&self, cursor_location: &TraversalProgress<'a>, _: &()) -> Ordering {
2806 match self {
2807 TraversalTarget::Path(path) => path.cmp(&cursor_location.max_path),
2808 TraversalTarget::PathSuccessor(path) => {
2809 if !cursor_location.max_path.starts_with(path) {
2810 Ordering::Equal
2811 } else {
2812 Ordering::Greater
2813 }
2814 }
2815 TraversalTarget::Count {
2816 count,
2817 include_dirs,
2818 include_ignored,
2819 } => Ord::cmp(
2820 count,
2821 &cursor_location.count(*include_dirs, *include_ignored),
2822 ),
2823 }
2824 }
2825}
2826
2827struct ChildEntriesIter<'a> {
2828 parent_path: &'a Path,
2829 traversal: Traversal<'a>,
2830}
2831
2832impl<'a> Iterator for ChildEntriesIter<'a> {
2833 type Item = &'a Entry;
2834
2835 fn next(&mut self) -> Option<Self::Item> {
2836 if let Some(item) = self.traversal.entry() {
2837 if item.path.starts_with(&self.parent_path) {
2838 self.traversal.advance_to_sibling();
2839 return Some(item);
2840 }
2841 }
2842 None
2843 }
2844}
2845
2846impl<'a> From<&'a Entry> for proto::Entry {
2847 fn from(entry: &'a Entry) -> Self {
2848 Self {
2849 id: entry.id as u64,
2850 is_dir: entry.is_dir(),
2851 path: entry.path.to_string_lossy().to_string(),
2852 inode: entry.inode,
2853 mtime: Some(entry.mtime.into()),
2854 is_symlink: entry.is_symlink,
2855 is_ignored: entry.is_ignored,
2856 }
2857 }
2858}
2859
2860impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
2861 type Error = anyhow::Error;
2862
2863 fn try_from((root_char_bag, entry): (&'a CharBag, proto::Entry)) -> Result<Self> {
2864 if let Some(mtime) = entry.mtime {
2865 let kind = if entry.is_dir {
2866 EntryKind::Dir
2867 } else {
2868 let mut char_bag = root_char_bag.clone();
2869 char_bag.extend(entry.path.chars().map(|c| c.to_ascii_lowercase()));
2870 EntryKind::File(char_bag)
2871 };
2872 let path: Arc<Path> = Arc::from(Path::new(&entry.path));
2873 Ok(Entry {
2874 id: entry.id as usize,
2875 kind,
2876 path: path.clone(),
2877 inode: entry.inode,
2878 mtime: mtime.into(),
2879 is_symlink: entry.is_symlink,
2880 is_ignored: entry.is_ignored,
2881 })
2882 } else {
2883 Err(anyhow!(
2884 "missing mtime in remote worktree entry {:?}",
2885 entry.path
2886 ))
2887 }
2888 }
2889}
2890
2891trait ToPointUtf16 {
2892 fn to_point_utf16(self) -> PointUtf16;
2893}
2894
2895impl ToPointUtf16 for lsp::Position {
2896 fn to_point_utf16(self) -> PointUtf16 {
2897 PointUtf16::new(self.line, self.character)
2898 }
2899}
2900
2901fn diagnostic_ranges<'a>(
2902 diagnostic: &'a lsp::Diagnostic,
2903 abs_path: &'a Path,
2904) -> impl 'a + Iterator<Item = Range<PointUtf16>> {
2905 diagnostic
2906 .related_information
2907 .iter()
2908 .flatten()
2909 .filter_map(move |info| {
2910 if info.location.uri.to_file_path().ok()? == abs_path {
2911 let info_start = PointUtf16::new(
2912 info.location.range.start.line,
2913 info.location.range.start.character,
2914 );
2915 let info_end = PointUtf16::new(
2916 info.location.range.end.line,
2917 info.location.range.end.character,
2918 );
2919 Some(info_start..info_end)
2920 } else {
2921 None
2922 }
2923 })
2924 .chain(Some(
2925 diagnostic.range.start.to_point_utf16()..diagnostic.range.end.to_point_utf16(),
2926 ))
2927}
2928
2929#[cfg(test)]
2930mod tests {
2931 use super::*;
2932 use crate::fs::FakeFs;
2933 use anyhow::Result;
2934 use client::test::{FakeHttpClient, FakeServer};
2935 use fs::RealFs;
2936 use language::{tree_sitter_rust, DiagnosticEntry, LanguageServerConfig};
2937 use language::{Diagnostic, LanguageConfig};
2938 use lsp::Url;
2939 use rand::prelude::*;
2940 use serde_json::json;
2941 use std::{cell::RefCell, rc::Rc};
2942 use std::{
2943 env,
2944 fmt::Write,
2945 time::{SystemTime, UNIX_EPOCH},
2946 };
2947 use text::Point;
2948 use unindent::Unindent as _;
2949 use util::test::temp_tree;
2950
2951 #[gpui::test]
2952 async fn test_traversal(mut cx: gpui::TestAppContext) {
2953 let fs = FakeFs::new();
2954 fs.insert_tree(
2955 "/root",
2956 json!({
2957 ".gitignore": "a/b\n",
2958 "a": {
2959 "b": "",
2960 "c": "",
2961 }
2962 }),
2963 )
2964 .await;
2965
2966 let client = Client::new();
2967 let http_client = FakeHttpClient::with_404_response();
2968 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
2969
2970 let tree = Worktree::open_local(
2971 client,
2972 user_store,
2973 Arc::from(Path::new("/root")),
2974 Arc::new(fs),
2975 Default::default(),
2976 &mut cx.to_async(),
2977 )
2978 .await
2979 .unwrap();
2980 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2981 .await;
2982
2983 tree.read_with(&cx, |tree, _| {
2984 assert_eq!(
2985 tree.entries(false)
2986 .map(|entry| entry.path.as_ref())
2987 .collect::<Vec<_>>(),
2988 vec![
2989 Path::new(""),
2990 Path::new(".gitignore"),
2991 Path::new("a"),
2992 Path::new("a/c"),
2993 ]
2994 );
2995 })
2996 }
2997
2998 #[gpui::test]
2999 async fn test_save_file(mut cx: gpui::TestAppContext) {
3000 let dir = temp_tree(json!({
3001 "file1": "the old contents",
3002 }));
3003
3004 let client = Client::new();
3005 let http_client = FakeHttpClient::with_404_response();
3006 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3007
3008 let tree = Worktree::open_local(
3009 client,
3010 user_store,
3011 dir.path(),
3012 Arc::new(RealFs),
3013 Default::default(),
3014 &mut cx.to_async(),
3015 )
3016 .await
3017 .unwrap();
3018 let buffer = tree
3019 .update(&mut cx, |tree, cx| tree.open_buffer("file1", cx))
3020 .await
3021 .unwrap();
3022 let save = buffer.update(&mut cx, |buffer, cx| {
3023 buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
3024 buffer.save(cx).unwrap()
3025 });
3026 save.await.unwrap();
3027
3028 let new_text = std::fs::read_to_string(dir.path().join("file1")).unwrap();
3029 assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
3030 }
3031
3032 #[gpui::test]
3033 async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
3034 let dir = temp_tree(json!({
3035 "file1": "the old contents",
3036 }));
3037 let file_path = dir.path().join("file1");
3038
3039 let client = Client::new();
3040 let http_client = FakeHttpClient::with_404_response();
3041 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3042
3043 let tree = Worktree::open_local(
3044 client,
3045 user_store,
3046 file_path.clone(),
3047 Arc::new(RealFs),
3048 Default::default(),
3049 &mut cx.to_async(),
3050 )
3051 .await
3052 .unwrap();
3053 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3054 .await;
3055 cx.read(|cx| assert_eq!(tree.read(cx).file_count(), 1));
3056
3057 let buffer = tree
3058 .update(&mut cx, |tree, cx| tree.open_buffer("", cx))
3059 .await
3060 .unwrap();
3061 let save = buffer.update(&mut cx, |buffer, cx| {
3062 buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
3063 buffer.save(cx).unwrap()
3064 });
3065 save.await.unwrap();
3066
3067 let new_text = std::fs::read_to_string(file_path).unwrap();
3068 assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
3069 }
3070
3071 #[gpui::test]
3072 async fn test_rescan_and_remote_updates(mut cx: gpui::TestAppContext) {
3073 let dir = temp_tree(json!({
3074 "a": {
3075 "file1": "",
3076 "file2": "",
3077 "file3": "",
3078 },
3079 "b": {
3080 "c": {
3081 "file4": "",
3082 "file5": "",
3083 }
3084 }
3085 }));
3086
3087 let user_id = 5;
3088 let mut client = Client::new();
3089 let server = FakeServer::for_client(user_id, &mut client, &cx).await;
3090 let user_store = server.build_user_store(client.clone(), &mut cx).await;
3091 let tree = Worktree::open_local(
3092 client,
3093 user_store.clone(),
3094 dir.path(),
3095 Arc::new(RealFs),
3096 Default::default(),
3097 &mut cx.to_async(),
3098 )
3099 .await
3100 .unwrap();
3101
3102 let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| {
3103 let buffer = tree.update(cx, |tree, cx| tree.open_buffer(path, cx));
3104 async move { buffer.await.unwrap() }
3105 };
3106 let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
3107 tree.read_with(cx, |tree, _| {
3108 tree.entry_for_path(path)
3109 .expect(&format!("no entry for path {}", path))
3110 .id
3111 })
3112 };
3113
3114 let buffer2 = buffer_for_path("a/file2", &mut cx).await;
3115 let buffer3 = buffer_for_path("a/file3", &mut cx).await;
3116 let buffer4 = buffer_for_path("b/c/file4", &mut cx).await;
3117 let buffer5 = buffer_for_path("b/c/file5", &mut cx).await;
3118
3119 let file2_id = id_for_path("a/file2", &cx);
3120 let file3_id = id_for_path("a/file3", &cx);
3121 let file4_id = id_for_path("b/c/file4", &cx);
3122
3123 // Wait for the initial scan.
3124 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3125 .await;
3126
3127 // Create a remote copy of this worktree.
3128 let initial_snapshot = tree.read_with(&cx, |tree, _| tree.snapshot());
3129 let remote = Worktree::remote(
3130 1,
3131 1,
3132 initial_snapshot.to_proto(),
3133 Client::new(),
3134 user_store,
3135 Default::default(),
3136 &mut cx.to_async(),
3137 )
3138 .await
3139 .unwrap();
3140
3141 cx.read(|cx| {
3142 assert!(!buffer2.read(cx).is_dirty());
3143 assert!(!buffer3.read(cx).is_dirty());
3144 assert!(!buffer4.read(cx).is_dirty());
3145 assert!(!buffer5.read(cx).is_dirty());
3146 });
3147
3148 // Rename and delete files and directories.
3149 tree.flush_fs_events(&cx).await;
3150 std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
3151 std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
3152 std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
3153 std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
3154 tree.flush_fs_events(&cx).await;
3155
3156 let expected_paths = vec![
3157 "a",
3158 "a/file1",
3159 "a/file2.new",
3160 "b",
3161 "d",
3162 "d/file3",
3163 "d/file4",
3164 ];
3165
3166 cx.read(|app| {
3167 assert_eq!(
3168 tree.read(app)
3169 .paths()
3170 .map(|p| p.to_str().unwrap())
3171 .collect::<Vec<_>>(),
3172 expected_paths
3173 );
3174
3175 assert_eq!(id_for_path("a/file2.new", &cx), file2_id);
3176 assert_eq!(id_for_path("d/file3", &cx), file3_id);
3177 assert_eq!(id_for_path("d/file4", &cx), file4_id);
3178
3179 assert_eq!(
3180 buffer2.read(app).file().unwrap().path().as_ref(),
3181 Path::new("a/file2.new")
3182 );
3183 assert_eq!(
3184 buffer3.read(app).file().unwrap().path().as_ref(),
3185 Path::new("d/file3")
3186 );
3187 assert_eq!(
3188 buffer4.read(app).file().unwrap().path().as_ref(),
3189 Path::new("d/file4")
3190 );
3191 assert_eq!(
3192 buffer5.read(app).file().unwrap().path().as_ref(),
3193 Path::new("b/c/file5")
3194 );
3195
3196 assert!(!buffer2.read(app).file().unwrap().is_deleted());
3197 assert!(!buffer3.read(app).file().unwrap().is_deleted());
3198 assert!(!buffer4.read(app).file().unwrap().is_deleted());
3199 assert!(buffer5.read(app).file().unwrap().is_deleted());
3200 });
3201
3202 // Update the remote worktree. Check that it becomes consistent with the
3203 // local worktree.
3204 remote.update(&mut cx, |remote, cx| {
3205 let update_message =
3206 tree.read(cx)
3207 .snapshot()
3208 .build_update(&initial_snapshot, 1, 1, true);
3209 remote
3210 .as_remote_mut()
3211 .unwrap()
3212 .snapshot
3213 .apply_update(update_message)
3214 .unwrap();
3215
3216 assert_eq!(
3217 remote
3218 .paths()
3219 .map(|p| p.to_str().unwrap())
3220 .collect::<Vec<_>>(),
3221 expected_paths
3222 );
3223 });
3224 }
3225
3226 #[gpui::test]
3227 async fn test_rescan_with_gitignore(mut cx: gpui::TestAppContext) {
3228 let dir = temp_tree(json!({
3229 ".git": {},
3230 ".gitignore": "ignored-dir\n",
3231 "tracked-dir": {
3232 "tracked-file1": "tracked contents",
3233 },
3234 "ignored-dir": {
3235 "ignored-file1": "ignored contents",
3236 }
3237 }));
3238
3239 let client = Client::new();
3240 let http_client = FakeHttpClient::with_404_response();
3241 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3242
3243 let tree = Worktree::open_local(
3244 client,
3245 user_store,
3246 dir.path(),
3247 Arc::new(RealFs),
3248 Default::default(),
3249 &mut cx.to_async(),
3250 )
3251 .await
3252 .unwrap();
3253 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3254 .await;
3255 tree.flush_fs_events(&cx).await;
3256 cx.read(|cx| {
3257 let tree = tree.read(cx);
3258 let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap();
3259 let ignored = tree.entry_for_path("ignored-dir/ignored-file1").unwrap();
3260 assert_eq!(tracked.is_ignored, false);
3261 assert_eq!(ignored.is_ignored, true);
3262 });
3263
3264 std::fs::write(dir.path().join("tracked-dir/tracked-file2"), "").unwrap();
3265 std::fs::write(dir.path().join("ignored-dir/ignored-file2"), "").unwrap();
3266 tree.flush_fs_events(&cx).await;
3267 cx.read(|cx| {
3268 let tree = tree.read(cx);
3269 let dot_git = tree.entry_for_path(".git").unwrap();
3270 let tracked = tree.entry_for_path("tracked-dir/tracked-file2").unwrap();
3271 let ignored = tree.entry_for_path("ignored-dir/ignored-file2").unwrap();
3272 assert_eq!(tracked.is_ignored, false);
3273 assert_eq!(ignored.is_ignored, true);
3274 assert_eq!(dot_git.is_ignored, true);
3275 });
3276 }
3277
3278 #[gpui::test]
3279 async fn test_buffer_deduping(mut cx: gpui::TestAppContext) {
3280 let user_id = 100;
3281 let mut client = Client::new();
3282 let server = FakeServer::for_client(user_id, &mut client, &cx).await;
3283 let user_store = server.build_user_store(client.clone(), &mut cx).await;
3284
3285 let fs = Arc::new(FakeFs::new());
3286 fs.insert_tree(
3287 "/the-dir",
3288 json!({
3289 "a.txt": "a-contents",
3290 "b.txt": "b-contents",
3291 }),
3292 )
3293 .await;
3294
3295 let worktree = Worktree::open_local(
3296 client.clone(),
3297 user_store,
3298 "/the-dir".as_ref(),
3299 fs,
3300 Default::default(),
3301 &mut cx.to_async(),
3302 )
3303 .await
3304 .unwrap();
3305
3306 // Spawn multiple tasks to open paths, repeating some paths.
3307 let (buffer_a_1, buffer_b, buffer_a_2) = worktree.update(&mut cx, |worktree, cx| {
3308 (
3309 worktree.open_buffer("a.txt", cx),
3310 worktree.open_buffer("b.txt", cx),
3311 worktree.open_buffer("a.txt", cx),
3312 )
3313 });
3314
3315 let buffer_a_1 = buffer_a_1.await.unwrap();
3316 let buffer_a_2 = buffer_a_2.await.unwrap();
3317 let buffer_b = buffer_b.await.unwrap();
3318 assert_eq!(buffer_a_1.read_with(&cx, |b, _| b.text()), "a-contents");
3319 assert_eq!(buffer_b.read_with(&cx, |b, _| b.text()), "b-contents");
3320
3321 // There is only one buffer per path.
3322 let buffer_a_id = buffer_a_1.id();
3323 assert_eq!(buffer_a_2.id(), buffer_a_id);
3324
3325 // Open the same path again while it is still open.
3326 drop(buffer_a_1);
3327 let buffer_a_3 = worktree
3328 .update(&mut cx, |worktree, cx| worktree.open_buffer("a.txt", cx))
3329 .await
3330 .unwrap();
3331
3332 // There's still only one buffer per path.
3333 assert_eq!(buffer_a_3.id(), buffer_a_id);
3334 }
3335
3336 #[gpui::test]
3337 async fn test_buffer_is_dirty(mut cx: gpui::TestAppContext) {
3338 use std::fs;
3339
3340 let dir = temp_tree(json!({
3341 "file1": "abc",
3342 "file2": "def",
3343 "file3": "ghi",
3344 }));
3345 let client = Client::new();
3346 let http_client = FakeHttpClient::with_404_response();
3347 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3348
3349 let tree = Worktree::open_local(
3350 client,
3351 user_store,
3352 dir.path(),
3353 Arc::new(RealFs),
3354 Default::default(),
3355 &mut cx.to_async(),
3356 )
3357 .await
3358 .unwrap();
3359 tree.flush_fs_events(&cx).await;
3360 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3361 .await;
3362
3363 let buffer1 = tree
3364 .update(&mut cx, |tree, cx| tree.open_buffer("file1", cx))
3365 .await
3366 .unwrap();
3367 let events = Rc::new(RefCell::new(Vec::new()));
3368
3369 // initially, the buffer isn't dirty.
3370 buffer1.update(&mut cx, |buffer, cx| {
3371 cx.subscribe(&buffer1, {
3372 let events = events.clone();
3373 move |_, _, event, _| events.borrow_mut().push(event.clone())
3374 })
3375 .detach();
3376
3377 assert!(!buffer.is_dirty());
3378 assert!(events.borrow().is_empty());
3379
3380 buffer.edit(vec![1..2], "", cx);
3381 });
3382
3383 // after the first edit, the buffer is dirty, and emits a dirtied event.
3384 buffer1.update(&mut cx, |buffer, cx| {
3385 assert!(buffer.text() == "ac");
3386 assert!(buffer.is_dirty());
3387 assert_eq!(
3388 *events.borrow(),
3389 &[language::Event::Edited, language::Event::Dirtied]
3390 );
3391 events.borrow_mut().clear();
3392 buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), None, cx);
3393 });
3394
3395 // after saving, the buffer is not dirty, and emits a saved event.
3396 buffer1.update(&mut cx, |buffer, cx| {
3397 assert!(!buffer.is_dirty());
3398 assert_eq!(*events.borrow(), &[language::Event::Saved]);
3399 events.borrow_mut().clear();
3400
3401 buffer.edit(vec![1..1], "B", cx);
3402 buffer.edit(vec![2..2], "D", cx);
3403 });
3404
3405 // after editing again, the buffer is dirty, and emits another dirty event.
3406 buffer1.update(&mut cx, |buffer, cx| {
3407 assert!(buffer.text() == "aBDc");
3408 assert!(buffer.is_dirty());
3409 assert_eq!(
3410 *events.borrow(),
3411 &[
3412 language::Event::Edited,
3413 language::Event::Dirtied,
3414 language::Event::Edited,
3415 ],
3416 );
3417 events.borrow_mut().clear();
3418
3419 // TODO - currently, after restoring the buffer to its
3420 // previously-saved state, the is still considered dirty.
3421 buffer.edit([1..3], "", cx);
3422 assert!(buffer.text() == "ac");
3423 assert!(buffer.is_dirty());
3424 });
3425
3426 assert_eq!(*events.borrow(), &[language::Event::Edited]);
3427
3428 // When a file is deleted, the buffer is considered dirty.
3429 let events = Rc::new(RefCell::new(Vec::new()));
3430 let buffer2 = tree
3431 .update(&mut cx, |tree, cx| tree.open_buffer("file2", cx))
3432 .await
3433 .unwrap();
3434 buffer2.update(&mut cx, |_, cx| {
3435 cx.subscribe(&buffer2, {
3436 let events = events.clone();
3437 move |_, _, event, _| events.borrow_mut().push(event.clone())
3438 })
3439 .detach();
3440 });
3441
3442 fs::remove_file(dir.path().join("file2")).unwrap();
3443 buffer2.condition(&cx, |b, _| b.is_dirty()).await;
3444 assert_eq!(
3445 *events.borrow(),
3446 &[language::Event::Dirtied, language::Event::FileHandleChanged]
3447 );
3448
3449 // When a file is already dirty when deleted, we don't emit a Dirtied event.
3450 let events = Rc::new(RefCell::new(Vec::new()));
3451 let buffer3 = tree
3452 .update(&mut cx, |tree, cx| tree.open_buffer("file3", cx))
3453 .await
3454 .unwrap();
3455 buffer3.update(&mut cx, |_, cx| {
3456 cx.subscribe(&buffer3, {
3457 let events = events.clone();
3458 move |_, _, event, _| events.borrow_mut().push(event.clone())
3459 })
3460 .detach();
3461 });
3462
3463 tree.flush_fs_events(&cx).await;
3464 buffer3.update(&mut cx, |buffer, cx| {
3465 buffer.edit(Some(0..0), "x", cx);
3466 });
3467 events.borrow_mut().clear();
3468 fs::remove_file(dir.path().join("file3")).unwrap();
3469 buffer3
3470 .condition(&cx, |_, _| !events.borrow().is_empty())
3471 .await;
3472 assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
3473 cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
3474 }
3475
3476 #[gpui::test]
3477 async fn test_buffer_file_changes_on_disk(mut cx: gpui::TestAppContext) {
3478 use std::fs;
3479
3480 let initial_contents = "aaa\nbbbbb\nc\n";
3481 let dir = temp_tree(json!({ "the-file": initial_contents }));
3482 let client = Client::new();
3483 let http_client = FakeHttpClient::with_404_response();
3484 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3485
3486 let tree = Worktree::open_local(
3487 client,
3488 user_store,
3489 dir.path(),
3490 Arc::new(RealFs),
3491 Default::default(),
3492 &mut cx.to_async(),
3493 )
3494 .await
3495 .unwrap();
3496 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3497 .await;
3498
3499 let abs_path = dir.path().join("the-file");
3500 let buffer = tree
3501 .update(&mut cx, |tree, cx| {
3502 tree.open_buffer(Path::new("the-file"), cx)
3503 })
3504 .await
3505 .unwrap();
3506
3507 // TODO
3508 // Add a cursor on each row.
3509 // let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
3510 // assert!(!buffer.is_dirty());
3511 // buffer.add_selection_set(
3512 // &(0..3)
3513 // .map(|row| Selection {
3514 // id: row as usize,
3515 // start: Point::new(row, 1),
3516 // end: Point::new(row, 1),
3517 // reversed: false,
3518 // goal: SelectionGoal::None,
3519 // })
3520 // .collect::<Vec<_>>(),
3521 // cx,
3522 // )
3523 // });
3524
3525 // Change the file on disk, adding two new lines of text, and removing
3526 // one line.
3527 buffer.read_with(&cx, |buffer, _| {
3528 assert!(!buffer.is_dirty());
3529 assert!(!buffer.has_conflict());
3530 });
3531 let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
3532 fs::write(&abs_path, new_contents).unwrap();
3533
3534 // Because the buffer was not modified, it is reloaded from disk. Its
3535 // contents are edited according to the diff between the old and new
3536 // file contents.
3537 buffer
3538 .condition(&cx, |buffer, _| buffer.text() == new_contents)
3539 .await;
3540
3541 buffer.update(&mut cx, |buffer, _| {
3542 assert_eq!(buffer.text(), new_contents);
3543 assert!(!buffer.is_dirty());
3544 assert!(!buffer.has_conflict());
3545
3546 // TODO
3547 // let cursor_positions = buffer
3548 // .selection_set(selection_set_id)
3549 // .unwrap()
3550 // .selections::<Point>(&*buffer)
3551 // .map(|selection| {
3552 // assert_eq!(selection.start, selection.end);
3553 // selection.start
3554 // })
3555 // .collect::<Vec<_>>();
3556 // assert_eq!(
3557 // cursor_positions,
3558 // [Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
3559 // );
3560 });
3561
3562 // Modify the buffer
3563 buffer.update(&mut cx, |buffer, cx| {
3564 buffer.edit(vec![0..0], " ", cx);
3565 assert!(buffer.is_dirty());
3566 assert!(!buffer.has_conflict());
3567 });
3568
3569 // Change the file on disk again, adding blank lines to the beginning.
3570 fs::write(&abs_path, "\n\n\nAAAA\naaa\nBB\nbbbbb\n").unwrap();
3571
3572 // Because the buffer is modified, it doesn't reload from disk, but is
3573 // marked as having a conflict.
3574 buffer
3575 .condition(&cx, |buffer, _| buffer.has_conflict())
3576 .await;
3577 }
3578
3579 #[gpui::test]
3580 async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
3581 let (language_server_config, mut fake_server) =
3582 LanguageServerConfig::fake(cx.background()).await;
3583 let mut languages = LanguageRegistry::new();
3584 languages.add(Arc::new(Language::new(
3585 LanguageConfig {
3586 name: "Rust".to_string(),
3587 path_suffixes: vec!["rs".to_string()],
3588 language_server: Some(language_server_config),
3589 ..Default::default()
3590 },
3591 Some(tree_sitter_rust::language()),
3592 )));
3593
3594 let dir = temp_tree(json!({
3595 "a.rs": "fn a() { A }",
3596 "b.rs": "const y: i32 = 1",
3597 }));
3598
3599 let client = Client::new();
3600 let http_client = FakeHttpClient::with_404_response();
3601 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3602
3603 let tree = Worktree::open_local(
3604 client,
3605 user_store,
3606 dir.path(),
3607 Arc::new(RealFs),
3608 Arc::new(languages),
3609 &mut cx.to_async(),
3610 )
3611 .await
3612 .unwrap();
3613 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3614 .await;
3615
3616 // Cause worktree to start the fake language server
3617 let _buffer = tree
3618 .update(&mut cx, |tree, cx| tree.open_buffer("b.rs", cx))
3619 .await
3620 .unwrap();
3621
3622 fake_server
3623 .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
3624 uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
3625 version: None,
3626 diagnostics: vec![lsp::Diagnostic {
3627 range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
3628 severity: Some(lsp::DiagnosticSeverity::ERROR),
3629 message: "undefined variable 'A'".to_string(),
3630 ..Default::default()
3631 }],
3632 })
3633 .await;
3634
3635 let buffer = tree
3636 .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))
3637 .await
3638 .unwrap();
3639
3640 buffer.read_with(&cx, |buffer, _| {
3641 let diagnostics = buffer
3642 .snapshot()
3643 .diagnostics_in_range::<_, Point>(0..buffer.len())
3644 .collect::<Vec<_>>();
3645 assert_eq!(
3646 diagnostics,
3647 &[DiagnosticEntry {
3648 range: Point::new(0, 9)..Point::new(0, 10),
3649 diagnostic: Diagnostic {
3650 severity: lsp::DiagnosticSeverity::ERROR,
3651 message: "undefined variable 'A'".to_string(),
3652 group_id: 0,
3653 is_primary: true,
3654 ..Default::default()
3655 }
3656 }]
3657 )
3658 });
3659 }
3660
3661 #[gpui::test]
3662 async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
3663 let fs = Arc::new(FakeFs::new());
3664 let client = Client::new();
3665 let http_client = FakeHttpClient::with_404_response();
3666 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3667
3668 fs.insert_tree(
3669 "/the-dir",
3670 json!({
3671 "a.rs": "
3672 fn foo(mut v: Vec<usize>) {
3673 for x in &v {
3674 v.push(1);
3675 }
3676 }
3677 "
3678 .unindent(),
3679 }),
3680 )
3681 .await;
3682
3683 let worktree = Worktree::open_local(
3684 client.clone(),
3685 user_store,
3686 "/the-dir".as_ref(),
3687 fs,
3688 Default::default(),
3689 &mut cx.to_async(),
3690 )
3691 .await
3692 .unwrap();
3693
3694 let buffer = worktree
3695 .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))
3696 .await
3697 .unwrap();
3698
3699 let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
3700 let message = lsp::PublishDiagnosticsParams {
3701 uri: buffer_uri.clone(),
3702 diagnostics: vec![
3703 lsp::Diagnostic {
3704 range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
3705 severity: Some(DiagnosticSeverity::WARNING),
3706 message: "error 1".to_string(),
3707 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3708 location: lsp::Location {
3709 uri: buffer_uri.clone(),
3710 range: lsp::Range::new(
3711 lsp::Position::new(1, 8),
3712 lsp::Position::new(1, 9),
3713 ),
3714 },
3715 message: "error 1 hint 1".to_string(),
3716 }]),
3717 ..Default::default()
3718 },
3719 lsp::Diagnostic {
3720 range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
3721 severity: Some(DiagnosticSeverity::HINT),
3722 message: "error 1 hint 1".to_string(),
3723 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3724 location: lsp::Location {
3725 uri: buffer_uri.clone(),
3726 range: lsp::Range::new(
3727 lsp::Position::new(1, 8),
3728 lsp::Position::new(1, 9),
3729 ),
3730 },
3731 message: "original diagnostic".to_string(),
3732 }]),
3733 ..Default::default()
3734 },
3735 lsp::Diagnostic {
3736 range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)),
3737 severity: Some(DiagnosticSeverity::ERROR),
3738 message: "error 2".to_string(),
3739 related_information: Some(vec![
3740 lsp::DiagnosticRelatedInformation {
3741 location: lsp::Location {
3742 uri: buffer_uri.clone(),
3743 range: lsp::Range::new(
3744 lsp::Position::new(1, 13),
3745 lsp::Position::new(1, 15),
3746 ),
3747 },
3748 message: "error 2 hint 1".to_string(),
3749 },
3750 lsp::DiagnosticRelatedInformation {
3751 location: lsp::Location {
3752 uri: buffer_uri.clone(),
3753 range: lsp::Range::new(
3754 lsp::Position::new(1, 13),
3755 lsp::Position::new(1, 15),
3756 ),
3757 },
3758 message: "error 2 hint 2".to_string(),
3759 },
3760 ]),
3761 ..Default::default()
3762 },
3763 lsp::Diagnostic {
3764 range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
3765 severity: Some(DiagnosticSeverity::HINT),
3766 message: "error 2 hint 1".to_string(),
3767 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3768 location: lsp::Location {
3769 uri: buffer_uri.clone(),
3770 range: lsp::Range::new(
3771 lsp::Position::new(2, 8),
3772 lsp::Position::new(2, 17),
3773 ),
3774 },
3775 message: "original diagnostic".to_string(),
3776 }]),
3777 ..Default::default()
3778 },
3779 lsp::Diagnostic {
3780 range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
3781 severity: Some(DiagnosticSeverity::HINT),
3782 message: "error 2 hint 2".to_string(),
3783 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3784 location: lsp::Location {
3785 uri: buffer_uri.clone(),
3786 range: lsp::Range::new(
3787 lsp::Position::new(2, 8),
3788 lsp::Position::new(2, 17),
3789 ),
3790 },
3791 message: "original diagnostic".to_string(),
3792 }]),
3793 ..Default::default()
3794 },
3795 ],
3796 version: None,
3797 };
3798
3799 worktree
3800 .update(&mut cx, |tree, cx| tree.update_diagnostics(message, cx))
3801 .unwrap();
3802 let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
3803
3804 assert_eq!(
3805 buffer
3806 .diagnostics_in_range::<_, Point>(0..buffer.len())
3807 .collect::<Vec<_>>(),
3808 &[
3809 DiagnosticEntry {
3810 range: Point::new(1, 8)..Point::new(1, 9),
3811 diagnostic: Diagnostic {
3812 severity: DiagnosticSeverity::WARNING,
3813 message: "error 1".to_string(),
3814 group_id: 0,
3815 is_primary: true,
3816 ..Default::default()
3817 }
3818 },
3819 DiagnosticEntry {
3820 range: Point::new(1, 8)..Point::new(1, 9),
3821 diagnostic: Diagnostic {
3822 severity: DiagnosticSeverity::HINT,
3823 message: "error 1 hint 1".to_string(),
3824 group_id: 0,
3825 is_primary: false,
3826 ..Default::default()
3827 }
3828 },
3829 DiagnosticEntry {
3830 range: Point::new(1, 13)..Point::new(1, 15),
3831 diagnostic: Diagnostic {
3832 severity: DiagnosticSeverity::HINT,
3833 message: "error 2 hint 1".to_string(),
3834 group_id: 1,
3835 is_primary: false,
3836 ..Default::default()
3837 }
3838 },
3839 DiagnosticEntry {
3840 range: Point::new(1, 13)..Point::new(1, 15),
3841 diagnostic: Diagnostic {
3842 severity: DiagnosticSeverity::HINT,
3843 message: "error 2 hint 2".to_string(),
3844 group_id: 1,
3845 is_primary: false,
3846 ..Default::default()
3847 }
3848 },
3849 DiagnosticEntry {
3850 range: Point::new(2, 8)..Point::new(2, 17),
3851 diagnostic: Diagnostic {
3852 severity: DiagnosticSeverity::ERROR,
3853 message: "error 2".to_string(),
3854 group_id: 1,
3855 is_primary: true,
3856 ..Default::default()
3857 }
3858 }
3859 ]
3860 );
3861
3862 assert_eq!(
3863 buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
3864 &[
3865 DiagnosticEntry {
3866 range: Point::new(1, 8)..Point::new(1, 9),
3867 diagnostic: Diagnostic {
3868 severity: DiagnosticSeverity::WARNING,
3869 message: "error 1".to_string(),
3870 group_id: 0,
3871 is_primary: true,
3872 ..Default::default()
3873 }
3874 },
3875 DiagnosticEntry {
3876 range: Point::new(1, 8)..Point::new(1, 9),
3877 diagnostic: Diagnostic {
3878 severity: DiagnosticSeverity::HINT,
3879 message: "error 1 hint 1".to_string(),
3880 group_id: 0,
3881 is_primary: false,
3882 ..Default::default()
3883 }
3884 },
3885 ]
3886 );
3887 assert_eq!(
3888 buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
3889 &[
3890 DiagnosticEntry {
3891 range: Point::new(1, 13)..Point::new(1, 15),
3892 diagnostic: Diagnostic {
3893 severity: DiagnosticSeverity::HINT,
3894 message: "error 2 hint 1".to_string(),
3895 group_id: 1,
3896 is_primary: false,
3897 ..Default::default()
3898 }
3899 },
3900 DiagnosticEntry {
3901 range: Point::new(1, 13)..Point::new(1, 15),
3902 diagnostic: Diagnostic {
3903 severity: DiagnosticSeverity::HINT,
3904 message: "error 2 hint 2".to_string(),
3905 group_id: 1,
3906 is_primary: false,
3907 ..Default::default()
3908 }
3909 },
3910 DiagnosticEntry {
3911 range: Point::new(2, 8)..Point::new(2, 17),
3912 diagnostic: Diagnostic {
3913 severity: DiagnosticSeverity::ERROR,
3914 message: "error 2".to_string(),
3915 group_id: 1,
3916 is_primary: true,
3917 ..Default::default()
3918 }
3919 }
3920 ]
3921 );
3922 }
3923
3924 #[gpui::test(iterations = 100)]
3925 fn test_random(mut rng: StdRng) {
3926 let operations = env::var("OPERATIONS")
3927 .map(|o| o.parse().unwrap())
3928 .unwrap_or(40);
3929 let initial_entries = env::var("INITIAL_ENTRIES")
3930 .map(|o| o.parse().unwrap())
3931 .unwrap_or(20);
3932
3933 let root_dir = tempdir::TempDir::new("worktree-test").unwrap();
3934 for _ in 0..initial_entries {
3935 randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap();
3936 }
3937 log::info!("Generated initial tree");
3938
3939 let (notify_tx, _notify_rx) = smol::channel::unbounded();
3940 let fs = Arc::new(RealFs);
3941 let next_entry_id = Arc::new(AtomicUsize::new(0));
3942 let mut initial_snapshot = Snapshot {
3943 id: 0,
3944 scan_id: 0,
3945 abs_path: root_dir.path().into(),
3946 entries_by_path: Default::default(),
3947 entries_by_id: Default::default(),
3948 removed_entry_ids: Default::default(),
3949 ignores: Default::default(),
3950 root_name: Default::default(),
3951 root_char_bag: Default::default(),
3952 next_entry_id: next_entry_id.clone(),
3953 };
3954 initial_snapshot.insert_entry(
3955 Entry::new(
3956 Path::new("").into(),
3957 &smol::block_on(fs.metadata(root_dir.path()))
3958 .unwrap()
3959 .unwrap(),
3960 &next_entry_id,
3961 Default::default(),
3962 ),
3963 fs.as_ref(),
3964 );
3965 let mut scanner = BackgroundScanner::new(
3966 Arc::new(Mutex::new(initial_snapshot.clone())),
3967 notify_tx,
3968 fs.clone(),
3969 Arc::new(gpui::executor::Background::new()),
3970 );
3971 smol::block_on(scanner.scan_dirs()).unwrap();
3972 scanner.snapshot().check_invariants();
3973
3974 let mut events = Vec::new();
3975 let mut snapshots = Vec::new();
3976 let mut mutations_len = operations;
3977 while mutations_len > 1 {
3978 if !events.is_empty() && rng.gen_bool(0.4) {
3979 let len = rng.gen_range(0..=events.len());
3980 let to_deliver = events.drain(0..len).collect::<Vec<_>>();
3981 log::info!("Delivering events: {:#?}", to_deliver);
3982 smol::block_on(scanner.process_events(to_deliver));
3983 scanner.snapshot().check_invariants();
3984 } else {
3985 events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
3986 mutations_len -= 1;
3987 }
3988
3989 if rng.gen_bool(0.2) {
3990 snapshots.push(scanner.snapshot());
3991 }
3992 }
3993 log::info!("Quiescing: {:#?}", events);
3994 smol::block_on(scanner.process_events(events));
3995 scanner.snapshot().check_invariants();
3996
3997 let (notify_tx, _notify_rx) = smol::channel::unbounded();
3998 let mut new_scanner = BackgroundScanner::new(
3999 Arc::new(Mutex::new(initial_snapshot)),
4000 notify_tx,
4001 scanner.fs.clone(),
4002 scanner.executor.clone(),
4003 );
4004 smol::block_on(new_scanner.scan_dirs()).unwrap();
4005 assert_eq!(
4006 scanner.snapshot().to_vec(true),
4007 new_scanner.snapshot().to_vec(true)
4008 );
4009
4010 for mut prev_snapshot in snapshots {
4011 let include_ignored = rng.gen::<bool>();
4012 if !include_ignored {
4013 let mut entries_by_path_edits = Vec::new();
4014 let mut entries_by_id_edits = Vec::new();
4015 for entry in prev_snapshot
4016 .entries_by_id
4017 .cursor::<()>()
4018 .filter(|e| e.is_ignored)
4019 {
4020 entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
4021 entries_by_id_edits.push(Edit::Remove(entry.id));
4022 }
4023
4024 prev_snapshot
4025 .entries_by_path
4026 .edit(entries_by_path_edits, &());
4027 prev_snapshot.entries_by_id.edit(entries_by_id_edits, &());
4028 }
4029
4030 let update = scanner
4031 .snapshot()
4032 .build_update(&prev_snapshot, 0, 0, include_ignored);
4033 prev_snapshot.apply_update(update).unwrap();
4034 assert_eq!(
4035 prev_snapshot.to_vec(true),
4036 scanner.snapshot().to_vec(include_ignored)
4037 );
4038 }
4039 }
4040
4041 fn randomly_mutate_tree(
4042 root_path: &Path,
4043 insertion_probability: f64,
4044 rng: &mut impl Rng,
4045 ) -> Result<Vec<fsevent::Event>> {
4046 let root_path = root_path.canonicalize().unwrap();
4047 let (dirs, files) = read_dir_recursive(root_path.clone());
4048
4049 let mut events = Vec::new();
4050 let mut record_event = |path: PathBuf| {
4051 events.push(fsevent::Event {
4052 event_id: SystemTime::now()
4053 .duration_since(UNIX_EPOCH)
4054 .unwrap()
4055 .as_secs(),
4056 flags: fsevent::StreamFlags::empty(),
4057 path,
4058 });
4059 };
4060
4061 if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
4062 let path = dirs.choose(rng).unwrap();
4063 let new_path = path.join(gen_name(rng));
4064
4065 if rng.gen() {
4066 log::info!("Creating dir {:?}", new_path.strip_prefix(root_path)?);
4067 std::fs::create_dir(&new_path)?;
4068 } else {
4069 log::info!("Creating file {:?}", new_path.strip_prefix(root_path)?);
4070 std::fs::write(&new_path, "")?;
4071 }
4072 record_event(new_path);
4073 } else if rng.gen_bool(0.05) {
4074 let ignore_dir_path = dirs.choose(rng).unwrap();
4075 let ignore_path = ignore_dir_path.join(&*GITIGNORE);
4076
4077 let (subdirs, subfiles) = read_dir_recursive(ignore_dir_path.clone());
4078 let files_to_ignore = {
4079 let len = rng.gen_range(0..=subfiles.len());
4080 subfiles.choose_multiple(rng, len)
4081 };
4082 let dirs_to_ignore = {
4083 let len = rng.gen_range(0..subdirs.len());
4084 subdirs.choose_multiple(rng, len)
4085 };
4086
4087 let mut ignore_contents = String::new();
4088 for path_to_ignore in files_to_ignore.chain(dirs_to_ignore) {
4089 write!(
4090 ignore_contents,
4091 "{}\n",
4092 path_to_ignore
4093 .strip_prefix(&ignore_dir_path)?
4094 .to_str()
4095 .unwrap()
4096 )
4097 .unwrap();
4098 }
4099 log::info!(
4100 "Creating {:?} with contents:\n{}",
4101 ignore_path.strip_prefix(&root_path)?,
4102 ignore_contents
4103 );
4104 std::fs::write(&ignore_path, ignore_contents).unwrap();
4105 record_event(ignore_path);
4106 } else {
4107 let old_path = {
4108 let file_path = files.choose(rng);
4109 let dir_path = dirs[1..].choose(rng);
4110 file_path.into_iter().chain(dir_path).choose(rng).unwrap()
4111 };
4112
4113 let is_rename = rng.gen();
4114 if is_rename {
4115 let new_path_parent = dirs
4116 .iter()
4117 .filter(|d| !d.starts_with(old_path))
4118 .choose(rng)
4119 .unwrap();
4120
4121 let overwrite_existing_dir =
4122 !old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
4123 let new_path = if overwrite_existing_dir {
4124 std::fs::remove_dir_all(&new_path_parent).ok();
4125 new_path_parent.to_path_buf()
4126 } else {
4127 new_path_parent.join(gen_name(rng))
4128 };
4129
4130 log::info!(
4131 "Renaming {:?} to {}{:?}",
4132 old_path.strip_prefix(&root_path)?,
4133 if overwrite_existing_dir {
4134 "overwrite "
4135 } else {
4136 ""
4137 },
4138 new_path.strip_prefix(&root_path)?
4139 );
4140 std::fs::rename(&old_path, &new_path)?;
4141 record_event(old_path.clone());
4142 record_event(new_path);
4143 } else if old_path.is_dir() {
4144 let (dirs, files) = read_dir_recursive(old_path.clone());
4145
4146 log::info!("Deleting dir {:?}", old_path.strip_prefix(&root_path)?);
4147 std::fs::remove_dir_all(&old_path).unwrap();
4148 for file in files {
4149 record_event(file);
4150 }
4151 for dir in dirs {
4152 record_event(dir);
4153 }
4154 } else {
4155 log::info!("Deleting file {:?}", old_path.strip_prefix(&root_path)?);
4156 std::fs::remove_file(old_path).unwrap();
4157 record_event(old_path.clone());
4158 }
4159 }
4160
4161 Ok(events)
4162 }
4163
4164 fn read_dir_recursive(path: PathBuf) -> (Vec<PathBuf>, Vec<PathBuf>) {
4165 let child_entries = std::fs::read_dir(&path).unwrap();
4166 let mut dirs = vec![path];
4167 let mut files = Vec::new();
4168 for child_entry in child_entries {
4169 let child_path = child_entry.unwrap().path();
4170 if child_path.is_dir() {
4171 let (child_dirs, child_files) = read_dir_recursive(child_path);
4172 dirs.extend(child_dirs);
4173 files.extend(child_files);
4174 } else {
4175 files.push(child_path);
4176 }
4177 }
4178 (dirs, files)
4179 }
4180
4181 fn gen_name(rng: &mut impl Rng) -> String {
4182 (0..6)
4183 .map(|_| rng.sample(rand::distributions::Alphanumeric))
4184 .map(char::from)
4185 .collect()
4186 }
4187
4188 impl Snapshot {
4189 fn check_invariants(&self) {
4190 let mut files = self.files(true, 0);
4191 let mut visible_files = self.files(false, 0);
4192 for entry in self.entries_by_path.cursor::<()>() {
4193 if entry.is_file() {
4194 assert_eq!(files.next().unwrap().inode, entry.inode);
4195 if !entry.is_ignored {
4196 assert_eq!(visible_files.next().unwrap().inode, entry.inode);
4197 }
4198 }
4199 }
4200 assert!(files.next().is_none());
4201 assert!(visible_files.next().is_none());
4202
4203 let mut bfs_paths = Vec::new();
4204 let mut stack = vec![Path::new("")];
4205 while let Some(path) = stack.pop() {
4206 bfs_paths.push(path);
4207 let ix = stack.len();
4208 for child_entry in self.child_entries(path) {
4209 stack.insert(ix, &child_entry.path);
4210 }
4211 }
4212
4213 let dfs_paths = self
4214 .entries_by_path
4215 .cursor::<()>()
4216 .map(|e| e.path.as_ref())
4217 .collect::<Vec<_>>();
4218 assert_eq!(bfs_paths, dfs_paths);
4219
4220 for (ignore_parent_path, _) in &self.ignores {
4221 assert!(self.entry_for_path(ignore_parent_path).is_some());
4222 assert!(self
4223 .entry_for_path(ignore_parent_path.join(&*GITIGNORE))
4224 .is_some());
4225 }
4226 }
4227
4228 fn to_vec(&self, include_ignored: bool) -> Vec<(&Path, u64, bool)> {
4229 let mut paths = Vec::new();
4230 for entry in self.entries_by_path.cursor::<()>() {
4231 if include_ignored || !entry.is_ignored {
4232 paths.push((entry.path.as_ref(), entry.inode, entry.is_ignored));
4233 }
4234 }
4235 paths.sort_by(|a, b| a.0.cmp(&b.0));
4236 paths
4237 }
4238 }
4239}