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