1pub mod fs;
2mod ignore;
3mod lsp_command;
4pub mod worktree;
5
6use anyhow::{anyhow, Context, Result};
7use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
8use clock::ReplicaId;
9use collections::{hash_map, HashMap, HashSet};
10use futures::{future::Shared, Future, FutureExt};
11use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
12use gpui::{
13 AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
14 UpgradeModelHandle, WeakModelHandle,
15};
16use language::{
17 range_from_lsp, Anchor, AnchorRangeExt, Bias, Buffer, CodeAction, CodeLabel, Completion,
18 Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16,
19 ToLspPosition, ToOffset, ToPointUtf16, Transaction,
20};
21use lsp::{DiagnosticSeverity, DocumentHighlightKind, LanguageServer};
22use lsp_command::*;
23use postage::{broadcast, prelude::Stream, sink::Sink, watch};
24use rand::prelude::*;
25use sha2::{Digest, Sha256};
26use smol::block_on;
27use std::{
28 convert::TryInto,
29 hash::Hash,
30 mem,
31 ops::Range,
32 path::{Component, Path, PathBuf},
33 sync::{atomic::AtomicBool, Arc},
34 time::Instant,
35};
36use util::{post_inc, ResultExt, TryFutureExt as _};
37
38pub use fs::*;
39pub use worktree::*;
40
41pub struct Project {
42 worktrees: Vec<WorktreeHandle>,
43 active_entry: Option<ProjectEntry>,
44 languages: Arc<LanguageRegistry>,
45 language_servers: HashMap<(WorktreeId, String), Arc<LanguageServer>>,
46 started_language_servers:
47 HashMap<(WorktreeId, String), Shared<Task<Option<Arc<LanguageServer>>>>>,
48 client: Arc<client::Client>,
49 user_store: ModelHandle<UserStore>,
50 fs: Arc<dyn Fs>,
51 client_state: ProjectClientState,
52 collaborators: HashMap<PeerId, Collaborator>,
53 subscriptions: Vec<client::Subscription>,
54 language_servers_with_diagnostics_running: isize,
55 open_buffers: HashMap<u64, OpenBuffer>,
56 opened_buffer: broadcast::Sender<()>,
57 loading_buffers: HashMap<
58 ProjectPath,
59 postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
60 >,
61 shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
62 nonce: u128,
63}
64
65enum OpenBuffer {
66 Loaded(WeakModelHandle<Buffer>),
67 Loading(Vec<Operation>),
68}
69
70enum WorktreeHandle {
71 Strong(ModelHandle<Worktree>),
72 Weak(WeakModelHandle<Worktree>),
73}
74
75enum ProjectClientState {
76 Local {
77 is_shared: bool,
78 remote_id_tx: watch::Sender<Option<u64>>,
79 remote_id_rx: watch::Receiver<Option<u64>>,
80 _maintain_remote_id_task: Task<Option<()>>,
81 },
82 Remote {
83 sharing_has_stopped: bool,
84 remote_id: u64,
85 replica_id: ReplicaId,
86 },
87}
88
89#[derive(Clone, Debug)]
90pub struct Collaborator {
91 pub user: Arc<User>,
92 pub peer_id: PeerId,
93 pub replica_id: ReplicaId,
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub enum Event {
98 ActiveEntryChanged(Option<ProjectEntry>),
99 WorktreeRemoved(WorktreeId),
100 DiskBasedDiagnosticsStarted,
101 DiskBasedDiagnosticsUpdated,
102 DiskBasedDiagnosticsFinished,
103 DiagnosticsUpdated(ProjectPath),
104}
105
106#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
107pub struct ProjectPath {
108 pub worktree_id: WorktreeId,
109 pub path: Arc<Path>,
110}
111
112#[derive(Clone, Debug, Default, PartialEq)]
113pub struct DiagnosticSummary {
114 pub error_count: usize,
115 pub warning_count: usize,
116 pub info_count: usize,
117 pub hint_count: usize,
118}
119
120#[derive(Debug)]
121pub struct Location {
122 pub buffer: ModelHandle<Buffer>,
123 pub range: Range<language::Anchor>,
124}
125
126#[derive(Debug)]
127pub struct DocumentHighlight {
128 pub range: Range<language::Anchor>,
129 pub kind: DocumentHighlightKind,
130}
131
132#[derive(Clone, Debug)]
133pub struct Symbol {
134 pub source_worktree_id: WorktreeId,
135 pub worktree_id: WorktreeId,
136 pub language_name: String,
137 pub path: PathBuf,
138 pub label: CodeLabel,
139 pub name: String,
140 pub kind: lsp::SymbolKind,
141 pub range: Range<PointUtf16>,
142 pub signature: [u8; 32],
143}
144
145#[derive(Default)]
146pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
147
148impl DiagnosticSummary {
149 fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
150 let mut this = Self {
151 error_count: 0,
152 warning_count: 0,
153 info_count: 0,
154 hint_count: 0,
155 };
156
157 for entry in diagnostics {
158 if entry.diagnostic.is_primary {
159 match entry.diagnostic.severity {
160 DiagnosticSeverity::ERROR => this.error_count += 1,
161 DiagnosticSeverity::WARNING => this.warning_count += 1,
162 DiagnosticSeverity::INFORMATION => this.info_count += 1,
163 DiagnosticSeverity::HINT => this.hint_count += 1,
164 _ => {}
165 }
166 }
167 }
168
169 this
170 }
171
172 pub fn to_proto(&self, path: Arc<Path>) -> proto::DiagnosticSummary {
173 proto::DiagnosticSummary {
174 path: path.to_string_lossy().to_string(),
175 error_count: self.error_count as u32,
176 warning_count: self.warning_count as u32,
177 info_count: self.info_count as u32,
178 hint_count: self.hint_count as u32,
179 }
180 }
181}
182
183#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
184pub struct ProjectEntry {
185 pub worktree_id: WorktreeId,
186 pub entry_id: usize,
187}
188
189impl Project {
190 pub fn init(client: &Arc<Client>) {
191 client.add_entity_message_handler(Self::handle_add_collaborator);
192 client.add_entity_message_handler(Self::handle_buffer_reloaded);
193 client.add_entity_message_handler(Self::handle_buffer_saved);
194 client.add_entity_message_handler(Self::handle_close_buffer);
195 client.add_entity_message_handler(Self::handle_disk_based_diagnostics_updated);
196 client.add_entity_message_handler(Self::handle_disk_based_diagnostics_updating);
197 client.add_entity_message_handler(Self::handle_remove_collaborator);
198 client.add_entity_message_handler(Self::handle_share_worktree);
199 client.add_entity_message_handler(Self::handle_unregister_worktree);
200 client.add_entity_message_handler(Self::handle_unshare_project);
201 client.add_entity_message_handler(Self::handle_update_buffer_file);
202 client.add_entity_message_handler(Self::handle_update_buffer);
203 client.add_entity_message_handler(Self::handle_update_diagnostic_summary);
204 client.add_entity_message_handler(Self::handle_update_worktree);
205 client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion);
206 client.add_entity_request_handler(Self::handle_apply_code_action);
207 client.add_entity_request_handler(Self::handle_format_buffers);
208 client.add_entity_request_handler(Self::handle_get_code_actions);
209 client.add_entity_request_handler(Self::handle_get_completions);
210 client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
211 client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
212 client.add_entity_request_handler(Self::handle_lsp_command::<GetReferences>);
213 client.add_entity_request_handler(Self::handle_lsp_command::<PrepareRename>);
214 client.add_entity_request_handler(Self::handle_lsp_command::<PerformRename>);
215 client.add_entity_request_handler(Self::handle_get_project_symbols);
216 client.add_entity_request_handler(Self::handle_open_buffer_for_symbol);
217 client.add_entity_request_handler(Self::handle_open_buffer);
218 client.add_entity_request_handler(Self::handle_save_buffer);
219 }
220
221 pub fn local(
222 client: Arc<Client>,
223 user_store: ModelHandle<UserStore>,
224 languages: Arc<LanguageRegistry>,
225 fs: Arc<dyn Fs>,
226 cx: &mut MutableAppContext,
227 ) -> ModelHandle<Self> {
228 cx.add_model(|cx: &mut ModelContext<Self>| {
229 let (remote_id_tx, remote_id_rx) = watch::channel();
230 let _maintain_remote_id_task = cx.spawn_weak({
231 let rpc = client.clone();
232 move |this, mut cx| {
233 async move {
234 let mut status = rpc.status();
235 while let Some(status) = status.recv().await {
236 if let Some(this) = this.upgrade(&cx) {
237 let remote_id = if let client::Status::Connected { .. } = status {
238 let response = rpc.request(proto::RegisterProject {}).await?;
239 Some(response.project_id)
240 } else {
241 None
242 };
243
244 if let Some(project_id) = remote_id {
245 let mut registrations = Vec::new();
246 this.update(&mut cx, |this, cx| {
247 for worktree in this.worktrees(cx).collect::<Vec<_>>() {
248 registrations.push(worktree.update(
249 cx,
250 |worktree, cx| {
251 let worktree = worktree.as_local_mut().unwrap();
252 worktree.register(project_id, cx)
253 },
254 ));
255 }
256 });
257 for registration in registrations {
258 registration.await?;
259 }
260 }
261 this.update(&mut cx, |this, cx| this.set_remote_id(remote_id, cx));
262 }
263 }
264 Ok(())
265 }
266 .log_err()
267 }
268 });
269
270 Self {
271 worktrees: Default::default(),
272 collaborators: Default::default(),
273 open_buffers: Default::default(),
274 loading_buffers: Default::default(),
275 shared_buffers: Default::default(),
276 client_state: ProjectClientState::Local {
277 is_shared: false,
278 remote_id_tx,
279 remote_id_rx,
280 _maintain_remote_id_task,
281 },
282 opened_buffer: broadcast::channel(1).0,
283 subscriptions: Vec::new(),
284 active_entry: None,
285 languages,
286 client,
287 user_store,
288 fs,
289 language_servers_with_diagnostics_running: 0,
290 language_servers: Default::default(),
291 started_language_servers: Default::default(),
292 nonce: StdRng::from_entropy().gen(),
293 }
294 })
295 }
296
297 pub async fn remote(
298 remote_id: u64,
299 client: Arc<Client>,
300 user_store: ModelHandle<UserStore>,
301 languages: Arc<LanguageRegistry>,
302 fs: Arc<dyn Fs>,
303 cx: &mut AsyncAppContext,
304 ) -> Result<ModelHandle<Self>> {
305 client.authenticate_and_connect(&cx).await?;
306
307 let response = client
308 .request(proto::JoinProject {
309 project_id: remote_id,
310 })
311 .await?;
312
313 let replica_id = response.replica_id as ReplicaId;
314
315 let mut worktrees = Vec::new();
316 for worktree in response.worktrees {
317 let (worktree, load_task) = cx
318 .update(|cx| Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx));
319 worktrees.push(worktree);
320 load_task.detach();
321 }
322
323 let this = cx.add_model(|cx| {
324 let mut this = Self {
325 worktrees: Vec::new(),
326 open_buffers: Default::default(),
327 loading_buffers: Default::default(),
328 opened_buffer: broadcast::channel(1).0,
329 shared_buffers: Default::default(),
330 active_entry: None,
331 collaborators: Default::default(),
332 languages,
333 user_store: user_store.clone(),
334 fs,
335 subscriptions: vec![client.add_model_for_remote_entity(remote_id, cx)],
336 client,
337 client_state: ProjectClientState::Remote {
338 sharing_has_stopped: false,
339 remote_id,
340 replica_id,
341 },
342 language_servers_with_diagnostics_running: 0,
343 language_servers: Default::default(),
344 started_language_servers: Default::default(),
345 nonce: StdRng::from_entropy().gen(),
346 };
347 for worktree in worktrees {
348 this.add_worktree(&worktree, cx);
349 }
350 this
351 });
352
353 let user_ids = response
354 .collaborators
355 .iter()
356 .map(|peer| peer.user_id)
357 .collect();
358 user_store
359 .update(cx, |user_store, cx| user_store.load_users(user_ids, cx))
360 .await?;
361 let mut collaborators = HashMap::default();
362 for message in response.collaborators {
363 let collaborator = Collaborator::from_proto(message, &user_store, cx).await?;
364 collaborators.insert(collaborator.peer_id, collaborator);
365 }
366
367 this.update(cx, |this, _| {
368 this.collaborators = collaborators;
369 });
370
371 Ok(this)
372 }
373
374 #[cfg(any(test, feature = "test-support"))]
375 pub fn test(fs: Arc<dyn Fs>, cx: &mut gpui::TestAppContext) -> ModelHandle<Project> {
376 let languages = Arc::new(LanguageRegistry::new());
377 let http_client = client::test::FakeHttpClient::with_404_response();
378 let client = client::Client::new(http_client.clone());
379 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
380 cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
381 }
382
383 #[cfg(any(test, feature = "test-support"))]
384 pub fn shared_buffer(&self, peer_id: PeerId, remote_id: u64) -> Option<ModelHandle<Buffer>> {
385 self.shared_buffers
386 .get(&peer_id)
387 .and_then(|buffers| buffers.get(&remote_id))
388 .cloned()
389 }
390
391 #[cfg(any(test, feature = "test-support"))]
392 pub fn has_buffered_operations(&self) -> bool {
393 self.open_buffers
394 .values()
395 .any(|buffer| matches!(buffer, OpenBuffer::Loading(_)))
396 }
397
398 #[cfg(any(test, feature = "test-support"))]
399 pub fn languages(&self) -> &Arc<LanguageRegistry> {
400 &self.languages
401 }
402
403 pub fn fs(&self) -> &Arc<dyn Fs> {
404 &self.fs
405 }
406
407 fn set_remote_id(&mut self, remote_id: Option<u64>, cx: &mut ModelContext<Self>) {
408 if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
409 *remote_id_tx.borrow_mut() = remote_id;
410 }
411
412 self.subscriptions.clear();
413 if let Some(remote_id) = remote_id {
414 self.subscriptions
415 .push(self.client.add_model_for_remote_entity(remote_id, cx));
416 }
417 }
418
419 pub fn remote_id(&self) -> Option<u64> {
420 match &self.client_state {
421 ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
422 ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
423 }
424 }
425
426 pub fn next_remote_id(&self) -> impl Future<Output = u64> {
427 let mut id = None;
428 let mut watch = None;
429 match &self.client_state {
430 ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
431 ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
432 }
433
434 async move {
435 if let Some(id) = id {
436 return id;
437 }
438 let mut watch = watch.unwrap();
439 loop {
440 let id = *watch.borrow();
441 if let Some(id) = id {
442 return id;
443 }
444 watch.recv().await;
445 }
446 }
447 }
448
449 pub fn replica_id(&self) -> ReplicaId {
450 match &self.client_state {
451 ProjectClientState::Local { .. } => 0,
452 ProjectClientState::Remote { replica_id, .. } => *replica_id,
453 }
454 }
455
456 pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
457 &self.collaborators
458 }
459
460 pub fn worktrees<'a>(
461 &'a self,
462 cx: &'a AppContext,
463 ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
464 self.worktrees
465 .iter()
466 .filter_map(move |worktree| worktree.upgrade(cx))
467 }
468
469 pub fn strong_worktrees<'a>(
470 &'a self,
471 cx: &'a AppContext,
472 ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
473 self.worktrees.iter().filter_map(|worktree| {
474 worktree.upgrade(cx).and_then(|worktree| {
475 if worktree.read(cx).is_weak() {
476 None
477 } else {
478 Some(worktree)
479 }
480 })
481 })
482 }
483
484 pub fn worktree_for_id(
485 &self,
486 id: WorktreeId,
487 cx: &AppContext,
488 ) -> Option<ModelHandle<Worktree>> {
489 self.worktrees(cx)
490 .find(|worktree| worktree.read(cx).id() == id)
491 }
492
493 pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
494 let rpc = self.client.clone();
495 cx.spawn(|this, mut cx| async move {
496 let project_id = this.update(&mut cx, |this, _| {
497 if let ProjectClientState::Local {
498 is_shared,
499 remote_id_rx,
500 ..
501 } = &mut this.client_state
502 {
503 *is_shared = true;
504 remote_id_rx
505 .borrow()
506 .ok_or_else(|| anyhow!("no project id"))
507 } else {
508 Err(anyhow!("can't share a remote project"))
509 }
510 })?;
511
512 rpc.request(proto::ShareProject { project_id }).await?;
513 let mut tasks = Vec::new();
514 this.update(&mut cx, |this, cx| {
515 for worktree in this.worktrees(cx).collect::<Vec<_>>() {
516 worktree.update(cx, |worktree, cx| {
517 let worktree = worktree.as_local_mut().unwrap();
518 tasks.push(worktree.share(project_id, cx));
519 });
520 }
521 });
522 for task in tasks {
523 task.await?;
524 }
525 this.update(&mut cx, |_, cx| cx.notify());
526 Ok(())
527 })
528 }
529
530 pub fn unshare(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
531 let rpc = self.client.clone();
532 cx.spawn(|this, mut cx| async move {
533 let project_id = this.update(&mut cx, |this, _| {
534 if let ProjectClientState::Local {
535 is_shared,
536 remote_id_rx,
537 ..
538 } = &mut this.client_state
539 {
540 *is_shared = false;
541 remote_id_rx
542 .borrow()
543 .ok_or_else(|| anyhow!("no project id"))
544 } else {
545 Err(anyhow!("can't share a remote project"))
546 }
547 })?;
548
549 rpc.send(proto::UnshareProject { project_id })?;
550 this.update(&mut cx, |this, cx| {
551 this.collaborators.clear();
552 this.shared_buffers.clear();
553 for worktree in this.worktrees(cx).collect::<Vec<_>>() {
554 worktree.update(cx, |worktree, _| {
555 worktree.as_local_mut().unwrap().unshare();
556 });
557 }
558 cx.notify()
559 });
560 Ok(())
561 })
562 }
563
564 pub fn is_read_only(&self) -> bool {
565 match &self.client_state {
566 ProjectClientState::Local { .. } => false,
567 ProjectClientState::Remote {
568 sharing_has_stopped,
569 ..
570 } => *sharing_has_stopped,
571 }
572 }
573
574 pub fn is_local(&self) -> bool {
575 match &self.client_state {
576 ProjectClientState::Local { .. } => true,
577 ProjectClientState::Remote { .. } => false,
578 }
579 }
580
581 pub fn is_remote(&self) -> bool {
582 !self.is_local()
583 }
584
585 pub fn open_buffer(
586 &mut self,
587 path: impl Into<ProjectPath>,
588 cx: &mut ModelContext<Self>,
589 ) -> Task<Result<ModelHandle<Buffer>>> {
590 let project_path = path.into();
591 let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
592 worktree
593 } else {
594 return Task::ready(Err(anyhow!("no such worktree")));
595 };
596
597 // If there is already a buffer for the given path, then return it.
598 let existing_buffer = self.get_open_buffer(&project_path, cx);
599 if let Some(existing_buffer) = existing_buffer {
600 return Task::ready(Ok(existing_buffer));
601 }
602
603 let mut loading_watch = match self.loading_buffers.entry(project_path.clone()) {
604 // If the given path is already being loaded, then wait for that existing
605 // task to complete and return the same buffer.
606 hash_map::Entry::Occupied(e) => e.get().clone(),
607
608 // Otherwise, record the fact that this path is now being loaded.
609 hash_map::Entry::Vacant(entry) => {
610 let (mut tx, rx) = postage::watch::channel();
611 entry.insert(rx.clone());
612
613 let load_buffer = if worktree.read(cx).is_local() {
614 self.open_local_buffer(&project_path.path, &worktree, cx)
615 } else {
616 self.open_remote_buffer(&project_path.path, &worktree, cx)
617 };
618
619 cx.spawn(move |this, mut cx| async move {
620 let load_result = load_buffer.await;
621 *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
622 // Record the fact that the buffer is no longer loading.
623 this.loading_buffers.remove(&project_path);
624 if this.loading_buffers.is_empty() {
625 this.open_buffers
626 .retain(|_, buffer| matches!(buffer, OpenBuffer::Loaded(_)))
627 }
628
629 let buffer = load_result.map_err(Arc::new)?;
630 Ok(buffer)
631 }));
632 })
633 .detach();
634 rx
635 }
636 };
637
638 cx.foreground().spawn(async move {
639 loop {
640 if let Some(result) = loading_watch.borrow().as_ref() {
641 match result {
642 Ok(buffer) => return Ok(buffer.clone()),
643 Err(error) => return Err(anyhow!("{}", error)),
644 }
645 }
646 loading_watch.recv().await;
647 }
648 })
649 }
650
651 fn open_local_buffer(
652 &mut self,
653 path: &Arc<Path>,
654 worktree: &ModelHandle<Worktree>,
655 cx: &mut ModelContext<Self>,
656 ) -> Task<Result<ModelHandle<Buffer>>> {
657 let load_buffer = worktree.update(cx, |worktree, cx| {
658 let worktree = worktree.as_local_mut().unwrap();
659 worktree.load_buffer(path, cx)
660 });
661 let worktree = worktree.downgrade();
662 cx.spawn(|this, mut cx| async move {
663 let buffer = load_buffer.await?;
664 let worktree = worktree
665 .upgrade(&cx)
666 .ok_or_else(|| anyhow!("worktree was removed"))?;
667 this.update(&mut cx, |this, cx| {
668 this.register_buffer(&buffer, Some(&worktree), cx)
669 })?;
670 Ok(buffer)
671 })
672 }
673
674 fn open_remote_buffer(
675 &mut self,
676 path: &Arc<Path>,
677 worktree: &ModelHandle<Worktree>,
678 cx: &mut ModelContext<Self>,
679 ) -> Task<Result<ModelHandle<Buffer>>> {
680 let rpc = self.client.clone();
681 let project_id = self.remote_id().unwrap();
682 let remote_worktree_id = worktree.read(cx).id();
683 let path = path.clone();
684 let path_string = path.to_string_lossy().to_string();
685 cx.spawn(|this, mut cx| async move {
686 let response = rpc
687 .request(proto::OpenBuffer {
688 project_id,
689 worktree_id: remote_worktree_id.to_proto(),
690 path: path_string,
691 })
692 .await?;
693 let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
694 this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
695 .await
696 })
697 }
698
699 fn open_local_buffer_via_lsp(
700 &mut self,
701 abs_path: lsp::Url,
702 lang_name: String,
703 lang_server: Arc<LanguageServer>,
704 cx: &mut ModelContext<Self>,
705 ) -> Task<Result<ModelHandle<Buffer>>> {
706 cx.spawn(|this, mut cx| async move {
707 let abs_path = abs_path
708 .to_file_path()
709 .map_err(|_| anyhow!("can't convert URI to path"))?;
710 let (worktree, relative_path) = if let Some(result) =
711 this.read_with(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
712 {
713 result
714 } else {
715 let worktree = this
716 .update(&mut cx, |this, cx| {
717 this.create_local_worktree(&abs_path, true, cx)
718 })
719 .await?;
720 this.update(&mut cx, |this, cx| {
721 this.language_servers
722 .insert((worktree.read(cx).id(), lang_name), lang_server);
723 });
724 (worktree, PathBuf::new())
725 };
726
727 let project_path = ProjectPath {
728 worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
729 path: relative_path.into(),
730 };
731 this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
732 .await
733 })
734 }
735
736 pub fn save_buffer_as(
737 &self,
738 buffer: ModelHandle<Buffer>,
739 abs_path: PathBuf,
740 cx: &mut ModelContext<Project>,
741 ) -> Task<Result<()>> {
742 let worktree_task = self.find_or_create_local_worktree(&abs_path, false, cx);
743 cx.spawn(|this, mut cx| async move {
744 let (worktree, path) = worktree_task.await?;
745 worktree
746 .update(&mut cx, |worktree, cx| {
747 worktree
748 .as_local_mut()
749 .unwrap()
750 .save_buffer_as(buffer.clone(), path, cx)
751 })
752 .await?;
753 this.update(&mut cx, |this, cx| {
754 this.assign_language_to_buffer(&buffer, Some(&worktree), cx);
755 });
756 Ok(())
757 })
758 }
759
760 #[cfg(any(test, feature = "test-support"))]
761 pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
762 let path = path.into();
763 if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
764 self.open_buffers.iter().any(|(_, buffer)| {
765 if let Some(buffer) = buffer.upgrade(cx) {
766 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
767 if file.worktree == worktree && file.path() == &path.path {
768 return true;
769 }
770 }
771 }
772 false
773 })
774 } else {
775 false
776 }
777 }
778
779 pub fn get_open_buffer(
780 &mut self,
781 path: &ProjectPath,
782 cx: &mut ModelContext<Self>,
783 ) -> Option<ModelHandle<Buffer>> {
784 let mut result = None;
785 let worktree = self.worktree_for_id(path.worktree_id, cx)?;
786 self.open_buffers.retain(|_, buffer| {
787 if let Some(buffer) = buffer.upgrade(cx) {
788 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
789 if file.worktree == worktree && file.path() == &path.path {
790 result = Some(buffer);
791 }
792 }
793 true
794 } else {
795 false
796 }
797 });
798 result
799 }
800
801 fn register_buffer(
802 &mut self,
803 buffer: &ModelHandle<Buffer>,
804 worktree: Option<&ModelHandle<Worktree>>,
805 cx: &mut ModelContext<Self>,
806 ) -> Result<()> {
807 match self.open_buffers.insert(
808 buffer.read(cx).remote_id(),
809 OpenBuffer::Loaded(buffer.downgrade()),
810 ) {
811 None => {}
812 Some(OpenBuffer::Loading(operations)) => {
813 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?
814 }
815 Some(OpenBuffer::Loaded(_)) => Err(anyhow!("registered the same buffer twice"))?,
816 }
817 self.assign_language_to_buffer(&buffer, worktree, cx);
818 Ok(())
819 }
820
821 fn assign_language_to_buffer(
822 &mut self,
823 buffer: &ModelHandle<Buffer>,
824 worktree: Option<&ModelHandle<Worktree>>,
825 cx: &mut ModelContext<Self>,
826 ) -> Option<()> {
827 let (path, full_path) = {
828 let file = buffer.read(cx).file()?;
829 (file.path().clone(), file.full_path(cx))
830 };
831
832 // If the buffer has a language, set it and start/assign the language server
833 if let Some(language) = self.languages.select_language(&full_path) {
834 buffer.update(cx, |buffer, cx| {
835 buffer.set_language(Some(language.clone()), cx);
836 });
837
838 // For local worktrees, start a language server if needed.
839 // Also assign the language server and any previously stored diagnostics to the buffer.
840 if let Some(local_worktree) = worktree.and_then(|w| w.read(cx).as_local()) {
841 let worktree_id = local_worktree.id();
842 let worktree_abs_path = local_worktree.abs_path().clone();
843 let buffer = buffer.downgrade();
844 let language_server =
845 self.start_language_server(worktree_id, worktree_abs_path, language, cx);
846
847 cx.spawn_weak(|_, mut cx| async move {
848 if let Some(language_server) = language_server.await {
849 if let Some(buffer) = buffer.upgrade(&cx) {
850 buffer.update(&mut cx, |buffer, cx| {
851 buffer.set_language_server(Some(language_server), cx);
852 });
853 }
854 }
855 })
856 .detach();
857 }
858 }
859
860 if let Some(local_worktree) = worktree.and_then(|w| w.read(cx).as_local()) {
861 if let Some(diagnostics) = local_worktree.diagnostics_for_path(&path) {
862 buffer.update(cx, |buffer, cx| {
863 buffer.update_diagnostics(diagnostics, None, cx).log_err();
864 });
865 }
866 }
867
868 None
869 }
870
871 fn start_language_server(
872 &mut self,
873 worktree_id: WorktreeId,
874 worktree_path: Arc<Path>,
875 language: Arc<Language>,
876 cx: &mut ModelContext<Self>,
877 ) -> Shared<Task<Option<Arc<LanguageServer>>>> {
878 enum LspEvent {
879 DiagnosticsStart,
880 DiagnosticsUpdate(lsp::PublishDiagnosticsParams),
881 DiagnosticsFinish,
882 }
883
884 let key = (worktree_id, language.name().to_string());
885 self.started_language_servers
886 .entry(key.clone())
887 .or_insert_with(|| {
888 let language_server = self.languages.start_language_server(
889 &language,
890 worktree_path,
891 self.client.http_client(),
892 cx,
893 );
894 let rpc = self.client.clone();
895 cx.spawn_weak(|this, mut cx| async move {
896 let language_server = language_server?.await.log_err()?;
897 if let Some(this) = this.upgrade(&cx) {
898 this.update(&mut cx, |this, _| {
899 this.language_servers.insert(key, language_server.clone());
900 });
901 }
902
903 let disk_based_sources = language
904 .disk_based_diagnostic_sources()
905 .cloned()
906 .unwrap_or_default();
907 let disk_based_diagnostics_progress_token =
908 language.disk_based_diagnostics_progress_token().cloned();
909 let has_disk_based_diagnostic_progress_token =
910 disk_based_diagnostics_progress_token.is_some();
911 let (diagnostics_tx, diagnostics_rx) = smol::channel::unbounded();
912
913 // Listen for `PublishDiagnostics` notifications.
914 language_server
915 .on_notification::<lsp::notification::PublishDiagnostics, _>({
916 let diagnostics_tx = diagnostics_tx.clone();
917 move |params| {
918 if !has_disk_based_diagnostic_progress_token {
919 block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
920 }
921 block_on(diagnostics_tx.send(LspEvent::DiagnosticsUpdate(params)))
922 .ok();
923 if !has_disk_based_diagnostic_progress_token {
924 block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
925 }
926 }
927 })
928 .detach();
929
930 // Listen for `Progress` notifications. Send an event when the language server
931 // transitions between running jobs and not running any jobs.
932 let mut running_jobs_for_this_server: i32 = 0;
933 language_server
934 .on_notification::<lsp::notification::Progress, _>(move |params| {
935 let token = match params.token {
936 lsp::NumberOrString::Number(_) => None,
937 lsp::NumberOrString::String(token) => Some(token),
938 };
939
940 if token == disk_based_diagnostics_progress_token {
941 match params.value {
942 lsp::ProgressParamsValue::WorkDone(progress) => {
943 match progress {
944 lsp::WorkDoneProgress::Begin(_) => {
945 running_jobs_for_this_server += 1;
946 if running_jobs_for_this_server == 1 {
947 block_on(
948 diagnostics_tx
949 .send(LspEvent::DiagnosticsStart),
950 )
951 .ok();
952 }
953 }
954 lsp::WorkDoneProgress::End(_) => {
955 running_jobs_for_this_server -= 1;
956 if running_jobs_for_this_server == 0 {
957 block_on(
958 diagnostics_tx
959 .send(LspEvent::DiagnosticsFinish),
960 )
961 .ok();
962 }
963 }
964 _ => {}
965 }
966 }
967 }
968 }
969 })
970 .detach();
971
972 // Process all the LSP events.
973 cx.spawn(|mut cx| async move {
974 while let Ok(message) = diagnostics_rx.recv().await {
975 let this = this.upgrade(&cx)?;
976 match message {
977 LspEvent::DiagnosticsStart => {
978 this.update(&mut cx, |this, cx| {
979 this.disk_based_diagnostics_started(cx);
980 if let Some(project_id) = this.remote_id() {
981 rpc.send(proto::DiskBasedDiagnosticsUpdating {
982 project_id,
983 })
984 .log_err();
985 }
986 });
987 }
988 LspEvent::DiagnosticsUpdate(mut params) => {
989 language.process_diagnostics(&mut params);
990 this.update(&mut cx, |this, cx| {
991 this.update_diagnostics(params, &disk_based_sources, cx)
992 .log_err();
993 });
994 }
995 LspEvent::DiagnosticsFinish => {
996 this.update(&mut cx, |this, cx| {
997 this.disk_based_diagnostics_finished(cx);
998 if let Some(project_id) = this.remote_id() {
999 rpc.send(proto::DiskBasedDiagnosticsUpdated {
1000 project_id,
1001 })
1002 .log_err();
1003 }
1004 });
1005 }
1006 }
1007 }
1008 Some(())
1009 })
1010 .detach();
1011
1012 Some(language_server)
1013 })
1014 .shared()
1015 })
1016 .clone()
1017 }
1018
1019 pub fn update_diagnostics(
1020 &mut self,
1021 params: lsp::PublishDiagnosticsParams,
1022 disk_based_sources: &HashSet<String>,
1023 cx: &mut ModelContext<Self>,
1024 ) -> Result<()> {
1025 let abs_path = params
1026 .uri
1027 .to_file_path()
1028 .map_err(|_| anyhow!("URI is not a file"))?;
1029 let mut next_group_id = 0;
1030 let mut diagnostics = Vec::default();
1031 let mut primary_diagnostic_group_ids = HashMap::default();
1032 let mut sources_by_group_id = HashMap::default();
1033 let mut supporting_diagnostic_severities = HashMap::default();
1034 for diagnostic in ¶ms.diagnostics {
1035 let source = diagnostic.source.as_ref();
1036 let code = diagnostic.code.as_ref().map(|code| match code {
1037 lsp::NumberOrString::Number(code) => code.to_string(),
1038 lsp::NumberOrString::String(code) => code.clone(),
1039 });
1040 let range = range_from_lsp(diagnostic.range);
1041 let is_supporting = diagnostic
1042 .related_information
1043 .as_ref()
1044 .map_or(false, |infos| {
1045 infos.iter().any(|info| {
1046 primary_diagnostic_group_ids.contains_key(&(
1047 source,
1048 code.clone(),
1049 range_from_lsp(info.location.range),
1050 ))
1051 })
1052 });
1053
1054 if is_supporting {
1055 if let Some(severity) = diagnostic.severity {
1056 supporting_diagnostic_severities
1057 .insert((source, code.clone(), range), severity);
1058 }
1059 } else {
1060 let group_id = post_inc(&mut next_group_id);
1061 let is_disk_based =
1062 source.map_or(false, |source| disk_based_sources.contains(source));
1063
1064 sources_by_group_id.insert(group_id, source);
1065 primary_diagnostic_group_ids
1066 .insert((source, code.clone(), range.clone()), group_id);
1067
1068 diagnostics.push(DiagnosticEntry {
1069 range,
1070 diagnostic: Diagnostic {
1071 code: code.clone(),
1072 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
1073 message: diagnostic.message.clone(),
1074 group_id,
1075 is_primary: true,
1076 is_valid: true,
1077 is_disk_based,
1078 },
1079 });
1080 if let Some(infos) = &diagnostic.related_information {
1081 for info in infos {
1082 if info.location.uri == params.uri && !info.message.is_empty() {
1083 let range = range_from_lsp(info.location.range);
1084 diagnostics.push(DiagnosticEntry {
1085 range,
1086 diagnostic: Diagnostic {
1087 code: code.clone(),
1088 severity: DiagnosticSeverity::INFORMATION,
1089 message: info.message.clone(),
1090 group_id,
1091 is_primary: false,
1092 is_valid: true,
1093 is_disk_based,
1094 },
1095 });
1096 }
1097 }
1098 }
1099 }
1100 }
1101
1102 for entry in &mut diagnostics {
1103 let diagnostic = &mut entry.diagnostic;
1104 if !diagnostic.is_primary {
1105 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
1106 if let Some(&severity) = supporting_diagnostic_severities.get(&(
1107 source,
1108 diagnostic.code.clone(),
1109 entry.range.clone(),
1110 )) {
1111 diagnostic.severity = severity;
1112 }
1113 }
1114 }
1115
1116 self.update_diagnostic_entries(abs_path, params.version, diagnostics, cx)?;
1117 Ok(())
1118 }
1119
1120 pub fn update_diagnostic_entries(
1121 &mut self,
1122 abs_path: PathBuf,
1123 version: Option<i32>,
1124 diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
1125 cx: &mut ModelContext<Project>,
1126 ) -> Result<(), anyhow::Error> {
1127 let (worktree, relative_path) = self
1128 .find_local_worktree(&abs_path, cx)
1129 .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
1130 let project_path = ProjectPath {
1131 worktree_id: worktree.read(cx).id(),
1132 path: relative_path.into(),
1133 };
1134
1135 for buffer in self.open_buffers.values() {
1136 if let Some(buffer) = buffer.upgrade(cx) {
1137 if buffer
1138 .read(cx)
1139 .file()
1140 .map_or(false, |file| *file.path() == project_path.path)
1141 {
1142 buffer.update(cx, |buffer, cx| {
1143 buffer.update_diagnostics(diagnostics.clone(), version, cx)
1144 })?;
1145 break;
1146 }
1147 }
1148 }
1149 worktree.update(cx, |worktree, cx| {
1150 worktree
1151 .as_local_mut()
1152 .ok_or_else(|| anyhow!("not a local worktree"))?
1153 .update_diagnostics(project_path.path.clone(), diagnostics, cx)
1154 })?;
1155 cx.emit(Event::DiagnosticsUpdated(project_path));
1156 Ok(())
1157 }
1158
1159 pub fn format(
1160 &self,
1161 buffers: HashSet<ModelHandle<Buffer>>,
1162 push_to_history: bool,
1163 cx: &mut ModelContext<Project>,
1164 ) -> Task<Result<ProjectTransaction>> {
1165 let mut local_buffers = Vec::new();
1166 let mut remote_buffers = None;
1167 for buffer_handle in buffers {
1168 let buffer = buffer_handle.read(cx);
1169 let worktree;
1170 if let Some(file) = File::from_dyn(buffer.file()) {
1171 worktree = file.worktree.clone();
1172 if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
1173 let lang_server;
1174 if let Some(lang) = buffer.language() {
1175 if let Some(server) = self
1176 .language_servers
1177 .get(&(worktree.read(cx).id(), lang.name().to_string()))
1178 {
1179 lang_server = server.clone();
1180 } else {
1181 return Task::ready(Ok(Default::default()));
1182 };
1183 } else {
1184 return Task::ready(Ok(Default::default()));
1185 }
1186
1187 local_buffers.push((buffer_handle, buffer_abs_path, lang_server));
1188 } else {
1189 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
1190 }
1191 } else {
1192 return Task::ready(Ok(Default::default()));
1193 }
1194 }
1195
1196 let remote_buffers = self.remote_id().zip(remote_buffers);
1197 let client = self.client.clone();
1198
1199 cx.spawn(|this, mut cx| async move {
1200 let mut project_transaction = ProjectTransaction::default();
1201
1202 if let Some((project_id, remote_buffers)) = remote_buffers {
1203 let response = client
1204 .request(proto::FormatBuffers {
1205 project_id,
1206 buffer_ids: remote_buffers
1207 .iter()
1208 .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
1209 .collect(),
1210 })
1211 .await?
1212 .transaction
1213 .ok_or_else(|| anyhow!("missing transaction"))?;
1214 project_transaction = this
1215 .update(&mut cx, |this, cx| {
1216 this.deserialize_project_transaction(response, push_to_history, cx)
1217 })
1218 .await?;
1219 }
1220
1221 for (buffer, buffer_abs_path, lang_server) in local_buffers {
1222 let lsp_edits = lang_server
1223 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
1224 text_document: lsp::TextDocumentIdentifier::new(
1225 lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
1226 ),
1227 options: Default::default(),
1228 work_done_progress_params: Default::default(),
1229 })
1230 .await?;
1231
1232 if let Some(lsp_edits) = lsp_edits {
1233 let edits = buffer
1234 .update(&mut cx, |buffer, cx| {
1235 buffer.edits_from_lsp(lsp_edits, None, cx)
1236 })
1237 .await?;
1238 buffer.update(&mut cx, |buffer, cx| {
1239 buffer.finalize_last_transaction();
1240 buffer.start_transaction();
1241 for (range, text) in edits {
1242 buffer.edit([range], text, cx);
1243 }
1244 if buffer.end_transaction(cx).is_some() {
1245 let transaction = buffer.finalize_last_transaction().unwrap().clone();
1246 if !push_to_history {
1247 buffer.forget_transaction(transaction.id);
1248 }
1249 project_transaction.0.insert(cx.handle(), transaction);
1250 }
1251 });
1252 }
1253 }
1254
1255 Ok(project_transaction)
1256 })
1257 }
1258
1259 pub fn definition<T: ToPointUtf16>(
1260 &self,
1261 buffer: &ModelHandle<Buffer>,
1262 position: T,
1263 cx: &mut ModelContext<Self>,
1264 ) -> Task<Result<Vec<Location>>> {
1265 let position = position.to_point_utf16(buffer.read(cx));
1266 self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
1267 }
1268
1269 pub fn references<T: ToPointUtf16>(
1270 &self,
1271 buffer: &ModelHandle<Buffer>,
1272 position: T,
1273 cx: &mut ModelContext<Self>,
1274 ) -> Task<Result<Vec<Location>>> {
1275 let position = position.to_point_utf16(buffer.read(cx));
1276 self.request_lsp(buffer.clone(), GetReferences { position }, cx)
1277 }
1278
1279 pub fn document_highlights<T: ToPointUtf16>(
1280 &self,
1281 buffer: &ModelHandle<Buffer>,
1282 position: T,
1283 cx: &mut ModelContext<Self>,
1284 ) -> Task<Result<Vec<DocumentHighlight>>> {
1285 let position = position.to_point_utf16(buffer.read(cx));
1286 self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
1287 }
1288
1289 pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
1290 if self.is_local() {
1291 let mut language_servers = HashMap::default();
1292 for ((worktree_id, language_name), language_server) in self.language_servers.iter() {
1293 if let Some((worktree, language)) = self
1294 .worktree_for_id(*worktree_id, cx)
1295 .and_then(|worktree| worktree.read(cx).as_local())
1296 .zip(self.languages.get_language(language_name))
1297 {
1298 language_servers
1299 .entry(Arc::as_ptr(language_server))
1300 .or_insert((
1301 language_server.clone(),
1302 *worktree_id,
1303 worktree.abs_path().clone(),
1304 language.clone(),
1305 ));
1306 }
1307 }
1308
1309 let mut requests = Vec::new();
1310 for (language_server, _, _, _) in language_servers.values() {
1311 requests.push(language_server.request::<lsp::request::WorkspaceSymbol>(
1312 lsp::WorkspaceSymbolParams {
1313 query: query.to_string(),
1314 ..Default::default()
1315 },
1316 ));
1317 }
1318
1319 cx.spawn_weak(|this, cx| async move {
1320 let responses = futures::future::try_join_all(requests).await?;
1321
1322 let mut symbols = Vec::new();
1323 if let Some(this) = this.upgrade(&cx) {
1324 this.read_with(&cx, |this, cx| {
1325 for ((_, source_worktree_id, worktree_abs_path, language), lsp_symbols) in
1326 language_servers.into_values().zip(responses)
1327 {
1328 symbols.extend(lsp_symbols.into_iter().flatten().filter_map(
1329 |lsp_symbol| {
1330 let abs_path = lsp_symbol.location.uri.to_file_path().ok()?;
1331 let mut worktree_id = source_worktree_id;
1332 let path;
1333 if let Some((worktree, rel_path)) =
1334 this.find_local_worktree(&abs_path, cx)
1335 {
1336 worktree_id = worktree.read(cx).id();
1337 path = rel_path;
1338 } else {
1339 path = relativize_path(&worktree_abs_path, &abs_path);
1340 }
1341
1342 let label = language
1343 .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind)
1344 .unwrap_or_else(|| {
1345 CodeLabel::plain(lsp_symbol.name.clone(), None)
1346 });
1347 let signature = this.symbol_signature(worktree_id, &path);
1348
1349 Some(Symbol {
1350 source_worktree_id,
1351 worktree_id,
1352 language_name: language.name().to_string(),
1353 name: lsp_symbol.name,
1354 kind: lsp_symbol.kind,
1355 label,
1356 path,
1357 range: range_from_lsp(lsp_symbol.location.range),
1358 signature,
1359 })
1360 },
1361 ));
1362 }
1363 })
1364 }
1365
1366 Ok(symbols)
1367 })
1368 } else if let Some(project_id) = self.remote_id() {
1369 let request = self.client.request(proto::GetProjectSymbols {
1370 project_id,
1371 query: query.to_string(),
1372 });
1373 cx.spawn_weak(|this, cx| async move {
1374 let response = request.await?;
1375 let mut symbols = Vec::new();
1376 if let Some(this) = this.upgrade(&cx) {
1377 this.read_with(&cx, |this, _| {
1378 symbols.extend(
1379 response
1380 .symbols
1381 .into_iter()
1382 .filter_map(|symbol| this.deserialize_symbol(symbol).log_err()),
1383 );
1384 })
1385 }
1386 Ok(symbols)
1387 })
1388 } else {
1389 Task::ready(Ok(Default::default()))
1390 }
1391 }
1392
1393 pub fn open_buffer_for_symbol(
1394 &mut self,
1395 symbol: &Symbol,
1396 cx: &mut ModelContext<Self>,
1397 ) -> Task<Result<ModelHandle<Buffer>>> {
1398 if self.is_local() {
1399 let language_server = if let Some(server) = self
1400 .language_servers
1401 .get(&(symbol.source_worktree_id, symbol.language_name.clone()))
1402 {
1403 server.clone()
1404 } else {
1405 return Task::ready(Err(anyhow!(
1406 "language server for worktree and language not found"
1407 )));
1408 };
1409
1410 let worktree_abs_path = if let Some(worktree_abs_path) = self
1411 .worktree_for_id(symbol.worktree_id, cx)
1412 .and_then(|worktree| worktree.read(cx).as_local())
1413 .map(|local_worktree| local_worktree.abs_path())
1414 {
1415 worktree_abs_path
1416 } else {
1417 return Task::ready(Err(anyhow!("worktree not found for symbol")));
1418 };
1419 let symbol_abs_path = worktree_abs_path.join(&symbol.path);
1420 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
1421 uri
1422 } else {
1423 return Task::ready(Err(anyhow!("invalid symbol path")));
1424 };
1425
1426 self.open_local_buffer_via_lsp(
1427 symbol_uri,
1428 symbol.language_name.clone(),
1429 language_server,
1430 cx,
1431 )
1432 } else if let Some(project_id) = self.remote_id() {
1433 let request = self.client.request(proto::OpenBufferForSymbol {
1434 project_id,
1435 symbol: Some(serialize_symbol(symbol)),
1436 });
1437 cx.spawn(|this, mut cx| async move {
1438 let response = request.await?;
1439 let buffer = response.buffer.ok_or_else(|| anyhow!("invalid buffer"))?;
1440 this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
1441 .await
1442 })
1443 } else {
1444 Task::ready(Err(anyhow!("project does not have a remote id")))
1445 }
1446 }
1447
1448 pub fn completions<T: ToPointUtf16>(
1449 &self,
1450 source_buffer_handle: &ModelHandle<Buffer>,
1451 position: T,
1452 cx: &mut ModelContext<Self>,
1453 ) -> Task<Result<Vec<Completion>>> {
1454 let source_buffer_handle = source_buffer_handle.clone();
1455 let source_buffer = source_buffer_handle.read(cx);
1456 let buffer_id = source_buffer.remote_id();
1457 let language = source_buffer.language().cloned();
1458 let worktree;
1459 let buffer_abs_path;
1460 if let Some(file) = File::from_dyn(source_buffer.file()) {
1461 worktree = file.worktree.clone();
1462 buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
1463 } else {
1464 return Task::ready(Ok(Default::default()));
1465 };
1466
1467 let position = position.to_point_utf16(source_buffer);
1468 let anchor = source_buffer.anchor_after(position);
1469
1470 if worktree.read(cx).as_local().is_some() {
1471 let buffer_abs_path = buffer_abs_path.unwrap();
1472 let lang_server = if let Some(server) = source_buffer.language_server().cloned() {
1473 server
1474 } else {
1475 return Task::ready(Ok(Default::default()));
1476 };
1477
1478 cx.spawn(|_, cx| async move {
1479 let completions = lang_server
1480 .request::<lsp::request::Completion>(lsp::CompletionParams {
1481 text_document_position: lsp::TextDocumentPositionParams::new(
1482 lsp::TextDocumentIdentifier::new(
1483 lsp::Url::from_file_path(buffer_abs_path).unwrap(),
1484 ),
1485 position.to_lsp_position(),
1486 ),
1487 context: Default::default(),
1488 work_done_progress_params: Default::default(),
1489 partial_result_params: Default::default(),
1490 })
1491 .await
1492 .context("lsp completion request failed")?;
1493
1494 let completions = if let Some(completions) = completions {
1495 match completions {
1496 lsp::CompletionResponse::Array(completions) => completions,
1497 lsp::CompletionResponse::List(list) => list.items,
1498 }
1499 } else {
1500 Default::default()
1501 };
1502
1503 source_buffer_handle.read_with(&cx, |this, _| {
1504 Ok(completions
1505 .into_iter()
1506 .filter_map(|lsp_completion| {
1507 let (old_range, new_text) = match lsp_completion.text_edit.as_ref()? {
1508 lsp::CompletionTextEdit::Edit(edit) => {
1509 (range_from_lsp(edit.range), edit.new_text.clone())
1510 }
1511 lsp::CompletionTextEdit::InsertAndReplace(_) => {
1512 log::info!("unsupported insert/replace completion");
1513 return None;
1514 }
1515 };
1516
1517 let clipped_start = this.clip_point_utf16(old_range.start, Bias::Left);
1518 let clipped_end = this.clip_point_utf16(old_range.end, Bias::Left);
1519 if clipped_start == old_range.start && clipped_end == old_range.end {
1520 Some(Completion {
1521 old_range: this.anchor_before(old_range.start)
1522 ..this.anchor_after(old_range.end),
1523 new_text,
1524 label: language
1525 .as_ref()
1526 .and_then(|l| l.label_for_completion(&lsp_completion))
1527 .unwrap_or_else(|| {
1528 CodeLabel::plain(
1529 lsp_completion.label.clone(),
1530 lsp_completion.filter_text.as_deref(),
1531 )
1532 }),
1533 lsp_completion,
1534 })
1535 } else {
1536 None
1537 }
1538 })
1539 .collect())
1540 })
1541 })
1542 } else if let Some(project_id) = self.remote_id() {
1543 let rpc = self.client.clone();
1544 let message = proto::GetCompletions {
1545 project_id,
1546 buffer_id,
1547 position: Some(language::proto::serialize_anchor(&anchor)),
1548 version: (&source_buffer.version()).into(),
1549 };
1550 cx.spawn_weak(|_, mut cx| async move {
1551 let response = rpc.request(message).await?;
1552
1553 source_buffer_handle
1554 .update(&mut cx, |buffer, _| {
1555 buffer.wait_for_version(response.version.into())
1556 })
1557 .await;
1558
1559 response
1560 .completions
1561 .into_iter()
1562 .map(|completion| {
1563 language::proto::deserialize_completion(completion, language.as_ref())
1564 })
1565 .collect()
1566 })
1567 } else {
1568 Task::ready(Ok(Default::default()))
1569 }
1570 }
1571
1572 pub fn apply_additional_edits_for_completion(
1573 &self,
1574 buffer_handle: ModelHandle<Buffer>,
1575 completion: Completion,
1576 push_to_history: bool,
1577 cx: &mut ModelContext<Self>,
1578 ) -> Task<Result<Option<Transaction>>> {
1579 let buffer = buffer_handle.read(cx);
1580 let buffer_id = buffer.remote_id();
1581
1582 if self.is_local() {
1583 let lang_server = if let Some(language_server) = buffer.language_server() {
1584 language_server.clone()
1585 } else {
1586 return Task::ready(Err(anyhow!("buffer does not have a language server")));
1587 };
1588
1589 cx.spawn(|_, mut cx| async move {
1590 let resolved_completion = lang_server
1591 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
1592 .await?;
1593 if let Some(edits) = resolved_completion.additional_text_edits {
1594 let edits = buffer_handle
1595 .update(&mut cx, |buffer, cx| buffer.edits_from_lsp(edits, None, cx))
1596 .await?;
1597 buffer_handle.update(&mut cx, |buffer, cx| {
1598 buffer.finalize_last_transaction();
1599 buffer.start_transaction();
1600 for (range, text) in edits {
1601 buffer.edit([range], text, cx);
1602 }
1603 let transaction = if buffer.end_transaction(cx).is_some() {
1604 let transaction = buffer.finalize_last_transaction().unwrap().clone();
1605 if !push_to_history {
1606 buffer.forget_transaction(transaction.id);
1607 }
1608 Some(transaction)
1609 } else {
1610 None
1611 };
1612 Ok(transaction)
1613 })
1614 } else {
1615 Ok(None)
1616 }
1617 })
1618 } else if let Some(project_id) = self.remote_id() {
1619 let client = self.client.clone();
1620 cx.spawn(|_, mut cx| async move {
1621 let response = client
1622 .request(proto::ApplyCompletionAdditionalEdits {
1623 project_id,
1624 buffer_id,
1625 completion: Some(language::proto::serialize_completion(&completion)),
1626 })
1627 .await?;
1628
1629 if let Some(transaction) = response.transaction {
1630 let transaction = language::proto::deserialize_transaction(transaction)?;
1631 buffer_handle
1632 .update(&mut cx, |buffer, _| {
1633 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
1634 })
1635 .await;
1636 if push_to_history {
1637 buffer_handle.update(&mut cx, |buffer, _| {
1638 buffer.push_transaction(transaction.clone(), Instant::now());
1639 });
1640 }
1641 Ok(Some(transaction))
1642 } else {
1643 Ok(None)
1644 }
1645 })
1646 } else {
1647 Task::ready(Err(anyhow!("project does not have a remote id")))
1648 }
1649 }
1650
1651 pub fn code_actions<T: ToOffset>(
1652 &self,
1653 buffer_handle: &ModelHandle<Buffer>,
1654 range: Range<T>,
1655 cx: &mut ModelContext<Self>,
1656 ) -> Task<Result<Vec<CodeAction>>> {
1657 let buffer_handle = buffer_handle.clone();
1658 let buffer = buffer_handle.read(cx);
1659 let buffer_id = buffer.remote_id();
1660 let worktree;
1661 let buffer_abs_path;
1662 if let Some(file) = File::from_dyn(buffer.file()) {
1663 worktree = file.worktree.clone();
1664 buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
1665 } else {
1666 return Task::ready(Ok(Default::default()));
1667 };
1668 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
1669
1670 if worktree.read(cx).as_local().is_some() {
1671 let buffer_abs_path = buffer_abs_path.unwrap();
1672 let lang_name;
1673 let lang_server;
1674 if let Some(lang) = buffer.language() {
1675 lang_name = lang.name().to_string();
1676 if let Some(server) = self
1677 .language_servers
1678 .get(&(worktree.read(cx).id(), lang_name.clone()))
1679 {
1680 lang_server = server.clone();
1681 } else {
1682 return Task::ready(Ok(Default::default()));
1683 };
1684 } else {
1685 return Task::ready(Ok(Default::default()));
1686 }
1687
1688 let lsp_range = lsp::Range::new(
1689 range.start.to_point_utf16(buffer).to_lsp_position(),
1690 range.end.to_point_utf16(buffer).to_lsp_position(),
1691 );
1692 cx.foreground().spawn(async move {
1693 Ok(lang_server
1694 .request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
1695 text_document: lsp::TextDocumentIdentifier::new(
1696 lsp::Url::from_file_path(buffer_abs_path).unwrap(),
1697 ),
1698 range: lsp_range,
1699 work_done_progress_params: Default::default(),
1700 partial_result_params: Default::default(),
1701 context: lsp::CodeActionContext {
1702 diagnostics: Default::default(),
1703 only: Some(vec![
1704 lsp::CodeActionKind::QUICKFIX,
1705 lsp::CodeActionKind::REFACTOR,
1706 lsp::CodeActionKind::REFACTOR_EXTRACT,
1707 ]),
1708 },
1709 })
1710 .await?
1711 .unwrap_or_default()
1712 .into_iter()
1713 .filter_map(|entry| {
1714 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1715 Some(CodeAction {
1716 range: range.clone(),
1717 lsp_action,
1718 })
1719 } else {
1720 None
1721 }
1722 })
1723 .collect())
1724 })
1725 } else if let Some(project_id) = self.remote_id() {
1726 let rpc = self.client.clone();
1727 cx.spawn_weak(|_, mut cx| async move {
1728 let response = rpc
1729 .request(proto::GetCodeActions {
1730 project_id,
1731 buffer_id,
1732 start: Some(language::proto::serialize_anchor(&range.start)),
1733 end: Some(language::proto::serialize_anchor(&range.end)),
1734 })
1735 .await?;
1736
1737 buffer_handle
1738 .update(&mut cx, |buffer, _| {
1739 buffer.wait_for_version(response.version.into())
1740 })
1741 .await;
1742
1743 response
1744 .actions
1745 .into_iter()
1746 .map(language::proto::deserialize_code_action)
1747 .collect()
1748 })
1749 } else {
1750 Task::ready(Ok(Default::default()))
1751 }
1752 }
1753
1754 pub fn apply_code_action(
1755 &self,
1756 buffer_handle: ModelHandle<Buffer>,
1757 mut action: CodeAction,
1758 push_to_history: bool,
1759 cx: &mut ModelContext<Self>,
1760 ) -> Task<Result<ProjectTransaction>> {
1761 if self.is_local() {
1762 let buffer = buffer_handle.read(cx);
1763 let lang_name = if let Some(lang) = buffer.language() {
1764 lang.name().to_string()
1765 } else {
1766 return Task::ready(Ok(Default::default()));
1767 };
1768 let lang_server = if let Some(language_server) = buffer.language_server() {
1769 language_server.clone()
1770 } else {
1771 return Task::ready(Err(anyhow!("buffer does not have a language server")));
1772 };
1773 let range = action.range.to_point_utf16(buffer);
1774
1775 cx.spawn(|this, mut cx| async move {
1776 if let Some(lsp_range) = action
1777 .lsp_action
1778 .data
1779 .as_mut()
1780 .and_then(|d| d.get_mut("codeActionParams"))
1781 .and_then(|d| d.get_mut("range"))
1782 {
1783 *lsp_range = serde_json::to_value(&lsp::Range::new(
1784 range.start.to_lsp_position(),
1785 range.end.to_lsp_position(),
1786 ))
1787 .unwrap();
1788 action.lsp_action = lang_server
1789 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
1790 .await?;
1791 } else {
1792 let actions = this
1793 .update(&mut cx, |this, cx| {
1794 this.code_actions(&buffer_handle, action.range, cx)
1795 })
1796 .await?;
1797 action.lsp_action = actions
1798 .into_iter()
1799 .find(|a| a.lsp_action.title == action.lsp_action.title)
1800 .ok_or_else(|| anyhow!("code action is outdated"))?
1801 .lsp_action;
1802 }
1803
1804 if let Some(edit) = action.lsp_action.edit {
1805 Self::deserialize_workspace_edit(
1806 this,
1807 edit,
1808 push_to_history,
1809 lang_name,
1810 lang_server,
1811 &mut cx,
1812 )
1813 .await
1814 } else {
1815 Ok(ProjectTransaction::default())
1816 }
1817 })
1818 } else if let Some(project_id) = self.remote_id() {
1819 let client = self.client.clone();
1820 let request = proto::ApplyCodeAction {
1821 project_id,
1822 buffer_id: buffer_handle.read(cx).remote_id(),
1823 action: Some(language::proto::serialize_code_action(&action)),
1824 };
1825 cx.spawn(|this, mut cx| async move {
1826 let response = client
1827 .request(request)
1828 .await?
1829 .transaction
1830 .ok_or_else(|| anyhow!("missing transaction"))?;
1831 this.update(&mut cx, |this, cx| {
1832 this.deserialize_project_transaction(response, push_to_history, cx)
1833 })
1834 .await
1835 })
1836 } else {
1837 Task::ready(Err(anyhow!("project does not have a remote id")))
1838 }
1839 }
1840
1841 async fn deserialize_workspace_edit(
1842 this: ModelHandle<Self>,
1843 edit: lsp::WorkspaceEdit,
1844 push_to_history: bool,
1845 language_name: String,
1846 language_server: Arc<LanguageServer>,
1847 cx: &mut AsyncAppContext,
1848 ) -> Result<ProjectTransaction> {
1849 let fs = this.read_with(cx, |this, _| this.fs.clone());
1850 let mut operations = Vec::new();
1851 if let Some(document_changes) = edit.document_changes {
1852 match document_changes {
1853 lsp::DocumentChanges::Edits(edits) => {
1854 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
1855 }
1856 lsp::DocumentChanges::Operations(ops) => operations = ops,
1857 }
1858 } else if let Some(changes) = edit.changes {
1859 operations.extend(changes.into_iter().map(|(uri, edits)| {
1860 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
1861 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
1862 uri,
1863 version: None,
1864 },
1865 edits: edits.into_iter().map(lsp::OneOf::Left).collect(),
1866 })
1867 }));
1868 }
1869
1870 let mut project_transaction = ProjectTransaction::default();
1871 for operation in operations {
1872 match operation {
1873 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
1874 let abs_path = op
1875 .uri
1876 .to_file_path()
1877 .map_err(|_| anyhow!("can't convert URI to path"))?;
1878
1879 if let Some(parent_path) = abs_path.parent() {
1880 fs.create_dir(parent_path).await?;
1881 }
1882 if abs_path.ends_with("/") {
1883 fs.create_dir(&abs_path).await?;
1884 } else {
1885 fs.create_file(&abs_path, op.options.map(Into::into).unwrap_or_default())
1886 .await?;
1887 }
1888 }
1889 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
1890 let source_abs_path = op
1891 .old_uri
1892 .to_file_path()
1893 .map_err(|_| anyhow!("can't convert URI to path"))?;
1894 let target_abs_path = op
1895 .new_uri
1896 .to_file_path()
1897 .map_err(|_| anyhow!("can't convert URI to path"))?;
1898 fs.rename(
1899 &source_abs_path,
1900 &target_abs_path,
1901 op.options.map(Into::into).unwrap_or_default(),
1902 )
1903 .await?;
1904 }
1905 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
1906 let abs_path = op
1907 .uri
1908 .to_file_path()
1909 .map_err(|_| anyhow!("can't convert URI to path"))?;
1910 let options = op.options.map(Into::into).unwrap_or_default();
1911 if abs_path.ends_with("/") {
1912 fs.remove_dir(&abs_path, options).await?;
1913 } else {
1914 fs.remove_file(&abs_path, options).await?;
1915 }
1916 }
1917 lsp::DocumentChangeOperation::Edit(op) => {
1918 let buffer_to_edit = this
1919 .update(cx, |this, cx| {
1920 this.open_local_buffer_via_lsp(
1921 op.text_document.uri,
1922 language_name.clone(),
1923 language_server.clone(),
1924 cx,
1925 )
1926 })
1927 .await?;
1928
1929 let edits = buffer_to_edit
1930 .update(cx, |buffer, cx| {
1931 let edits = op.edits.into_iter().map(|edit| match edit {
1932 lsp::OneOf::Left(edit) => edit,
1933 lsp::OneOf::Right(edit) => edit.text_edit,
1934 });
1935 buffer.edits_from_lsp(edits, op.text_document.version, cx)
1936 })
1937 .await?;
1938
1939 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
1940 buffer.finalize_last_transaction();
1941 buffer.start_transaction();
1942 for (range, text) in edits {
1943 buffer.edit([range], text, cx);
1944 }
1945 let transaction = if buffer.end_transaction(cx).is_some() {
1946 let transaction = buffer.finalize_last_transaction().unwrap().clone();
1947 if !push_to_history {
1948 buffer.forget_transaction(transaction.id);
1949 }
1950 Some(transaction)
1951 } else {
1952 None
1953 };
1954
1955 transaction
1956 });
1957 if let Some(transaction) = transaction {
1958 project_transaction.0.insert(buffer_to_edit, transaction);
1959 }
1960 }
1961 }
1962 }
1963
1964 Ok(project_transaction)
1965 }
1966
1967 pub fn prepare_rename<T: ToPointUtf16>(
1968 &self,
1969 buffer: ModelHandle<Buffer>,
1970 position: T,
1971 cx: &mut ModelContext<Self>,
1972 ) -> Task<Result<Option<Range<Anchor>>>> {
1973 let position = position.to_point_utf16(buffer.read(cx));
1974 self.request_lsp(buffer, PrepareRename { position }, cx)
1975 }
1976
1977 pub fn perform_rename<T: ToPointUtf16>(
1978 &self,
1979 buffer: ModelHandle<Buffer>,
1980 position: T,
1981 new_name: String,
1982 push_to_history: bool,
1983 cx: &mut ModelContext<Self>,
1984 ) -> Task<Result<ProjectTransaction>> {
1985 let position = position.to_point_utf16(buffer.read(cx));
1986 self.request_lsp(
1987 buffer,
1988 PerformRename {
1989 position,
1990 new_name,
1991 push_to_history,
1992 },
1993 cx,
1994 )
1995 }
1996
1997 fn request_lsp<R: LspCommand>(
1998 &self,
1999 buffer_handle: ModelHandle<Buffer>,
2000 request: R,
2001 cx: &mut ModelContext<Self>,
2002 ) -> Task<Result<R::Response>>
2003 where
2004 <R::LspRequest as lsp::request::Request>::Result: Send,
2005 {
2006 let buffer = buffer_handle.read(cx);
2007 if self.is_local() {
2008 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
2009 if let Some((file, language_server)) = file.zip(buffer.language_server().cloned()) {
2010 let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
2011 return cx.spawn(|this, cx| async move {
2012 let response = language_server
2013 .request::<R::LspRequest>(lsp_params)
2014 .await
2015 .context("lsp request failed")?;
2016 request
2017 .response_from_lsp(response, this, buffer_handle, cx)
2018 .await
2019 });
2020 }
2021 } else if let Some(project_id) = self.remote_id() {
2022 let rpc = self.client.clone();
2023 let message = request.to_proto(project_id, buffer);
2024 return cx.spawn(|this, cx| async move {
2025 let response = rpc.request(message).await?;
2026 request
2027 .response_from_proto(response, this, buffer_handle, cx)
2028 .await
2029 });
2030 }
2031 Task::ready(Ok(Default::default()))
2032 }
2033
2034 pub fn find_or_create_local_worktree(
2035 &self,
2036 abs_path: impl AsRef<Path>,
2037 weak: bool,
2038 cx: &mut ModelContext<Self>,
2039 ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
2040 let abs_path = abs_path.as_ref();
2041 if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
2042 Task::ready(Ok((tree.clone(), relative_path.into())))
2043 } else {
2044 let worktree = self.create_local_worktree(abs_path, weak, cx);
2045 cx.foreground()
2046 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
2047 }
2048 }
2049
2050 pub fn find_local_worktree(
2051 &self,
2052 abs_path: &Path,
2053 cx: &AppContext,
2054 ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
2055 for tree in self.worktrees(cx) {
2056 if let Some(relative_path) = tree
2057 .read(cx)
2058 .as_local()
2059 .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
2060 {
2061 return Some((tree.clone(), relative_path.into()));
2062 }
2063 }
2064 None
2065 }
2066
2067 pub fn is_shared(&self) -> bool {
2068 match &self.client_state {
2069 ProjectClientState::Local { is_shared, .. } => *is_shared,
2070 ProjectClientState::Remote { .. } => false,
2071 }
2072 }
2073
2074 fn create_local_worktree(
2075 &self,
2076 abs_path: impl AsRef<Path>,
2077 weak: bool,
2078 cx: &mut ModelContext<Self>,
2079 ) -> Task<Result<ModelHandle<Worktree>>> {
2080 let fs = self.fs.clone();
2081 let client = self.client.clone();
2082 let path = Arc::from(abs_path.as_ref());
2083 cx.spawn(|project, mut cx| async move {
2084 let worktree = Worktree::local(client.clone(), path, weak, fs, &mut cx).await?;
2085
2086 let (remote_project_id, is_shared) = project.update(&mut cx, |project, cx| {
2087 project.add_worktree(&worktree, cx);
2088 (project.remote_id(), project.is_shared())
2089 });
2090
2091 if let Some(project_id) = remote_project_id {
2092 worktree
2093 .update(&mut cx, |worktree, cx| {
2094 worktree.as_local_mut().unwrap().register(project_id, cx)
2095 })
2096 .await?;
2097 if is_shared {
2098 worktree
2099 .update(&mut cx, |worktree, cx| {
2100 worktree.as_local_mut().unwrap().share(project_id, cx)
2101 })
2102 .await?;
2103 }
2104 }
2105
2106 Ok(worktree)
2107 })
2108 }
2109
2110 pub fn remove_worktree(&mut self, id: WorktreeId, cx: &mut ModelContext<Self>) {
2111 self.worktrees.retain(|worktree| {
2112 worktree
2113 .upgrade(cx)
2114 .map_or(false, |w| w.read(cx).id() != id)
2115 });
2116 cx.notify();
2117 }
2118
2119 fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
2120 cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
2121 if worktree.read(cx).is_local() {
2122 cx.subscribe(&worktree, |this, worktree, _, cx| {
2123 this.update_local_worktree_buffers(worktree, cx);
2124 })
2125 .detach();
2126 }
2127
2128 let push_weak_handle = {
2129 let worktree = worktree.read(cx);
2130 worktree.is_local() && worktree.is_weak()
2131 };
2132 if push_weak_handle {
2133 cx.observe_release(&worktree, |this, cx| {
2134 this.worktrees
2135 .retain(|worktree| worktree.upgrade(cx).is_some());
2136 cx.notify();
2137 })
2138 .detach();
2139 self.worktrees
2140 .push(WorktreeHandle::Weak(worktree.downgrade()));
2141 } else {
2142 self.worktrees
2143 .push(WorktreeHandle::Strong(worktree.clone()));
2144 }
2145 cx.notify();
2146 }
2147
2148 fn update_local_worktree_buffers(
2149 &mut self,
2150 worktree_handle: ModelHandle<Worktree>,
2151 cx: &mut ModelContext<Self>,
2152 ) {
2153 let snapshot = worktree_handle.read(cx).snapshot();
2154 let mut buffers_to_delete = Vec::new();
2155 for (buffer_id, buffer) in &self.open_buffers {
2156 if let Some(buffer) = buffer.upgrade(cx) {
2157 buffer.update(cx, |buffer, cx| {
2158 if let Some(old_file) = File::from_dyn(buffer.file()) {
2159 if old_file.worktree != worktree_handle {
2160 return;
2161 }
2162
2163 let new_file = if let Some(entry) = old_file
2164 .entry_id
2165 .and_then(|entry_id| snapshot.entry_for_id(entry_id))
2166 {
2167 File {
2168 is_local: true,
2169 entry_id: Some(entry.id),
2170 mtime: entry.mtime,
2171 path: entry.path.clone(),
2172 worktree: worktree_handle.clone(),
2173 }
2174 } else if let Some(entry) =
2175 snapshot.entry_for_path(old_file.path().as_ref())
2176 {
2177 File {
2178 is_local: true,
2179 entry_id: Some(entry.id),
2180 mtime: entry.mtime,
2181 path: entry.path.clone(),
2182 worktree: worktree_handle.clone(),
2183 }
2184 } else {
2185 File {
2186 is_local: true,
2187 entry_id: None,
2188 path: old_file.path().clone(),
2189 mtime: old_file.mtime(),
2190 worktree: worktree_handle.clone(),
2191 }
2192 };
2193
2194 if let Some(project_id) = self.remote_id() {
2195 self.client
2196 .send(proto::UpdateBufferFile {
2197 project_id,
2198 buffer_id: *buffer_id as u64,
2199 file: Some(new_file.to_proto()),
2200 })
2201 .log_err();
2202 }
2203 buffer.file_updated(Box::new(new_file), cx).detach();
2204 }
2205 });
2206 } else {
2207 buffers_to_delete.push(*buffer_id);
2208 }
2209 }
2210
2211 for buffer_id in buffers_to_delete {
2212 self.open_buffers.remove(&buffer_id);
2213 }
2214 }
2215
2216 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
2217 let new_active_entry = entry.and_then(|project_path| {
2218 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
2219 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
2220 Some(ProjectEntry {
2221 worktree_id: project_path.worktree_id,
2222 entry_id: entry.id,
2223 })
2224 });
2225 if new_active_entry != self.active_entry {
2226 self.active_entry = new_active_entry;
2227 cx.emit(Event::ActiveEntryChanged(new_active_entry));
2228 }
2229 }
2230
2231 pub fn is_running_disk_based_diagnostics(&self) -> bool {
2232 self.language_servers_with_diagnostics_running > 0
2233 }
2234
2235 pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
2236 let mut summary = DiagnosticSummary::default();
2237 for (_, path_summary) in self.diagnostic_summaries(cx) {
2238 summary.error_count += path_summary.error_count;
2239 summary.warning_count += path_summary.warning_count;
2240 summary.info_count += path_summary.info_count;
2241 summary.hint_count += path_summary.hint_count;
2242 }
2243 summary
2244 }
2245
2246 pub fn diagnostic_summaries<'a>(
2247 &'a self,
2248 cx: &'a AppContext,
2249 ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
2250 self.worktrees(cx).flat_map(move |worktree| {
2251 let worktree = worktree.read(cx);
2252 let worktree_id = worktree.id();
2253 worktree
2254 .diagnostic_summaries()
2255 .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
2256 })
2257 }
2258
2259 pub fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
2260 self.language_servers_with_diagnostics_running += 1;
2261 if self.language_servers_with_diagnostics_running == 1 {
2262 cx.emit(Event::DiskBasedDiagnosticsStarted);
2263 }
2264 }
2265
2266 pub fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
2267 cx.emit(Event::DiskBasedDiagnosticsUpdated);
2268 self.language_servers_with_diagnostics_running -= 1;
2269 if self.language_servers_with_diagnostics_running == 0 {
2270 cx.emit(Event::DiskBasedDiagnosticsFinished);
2271 }
2272 }
2273
2274 pub fn active_entry(&self) -> Option<ProjectEntry> {
2275 self.active_entry
2276 }
2277
2278 // RPC message handlers
2279
2280 async fn handle_unshare_project(
2281 this: ModelHandle<Self>,
2282 _: TypedEnvelope<proto::UnshareProject>,
2283 _: Arc<Client>,
2284 mut cx: AsyncAppContext,
2285 ) -> Result<()> {
2286 this.update(&mut cx, |this, cx| {
2287 if let ProjectClientState::Remote {
2288 sharing_has_stopped,
2289 ..
2290 } = &mut this.client_state
2291 {
2292 *sharing_has_stopped = true;
2293 this.collaborators.clear();
2294 cx.notify();
2295 } else {
2296 unreachable!()
2297 }
2298 });
2299
2300 Ok(())
2301 }
2302
2303 async fn handle_add_collaborator(
2304 this: ModelHandle<Self>,
2305 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
2306 _: Arc<Client>,
2307 mut cx: AsyncAppContext,
2308 ) -> Result<()> {
2309 let user_store = this.read_with(&cx, |this, _| this.user_store.clone());
2310 let collaborator = envelope
2311 .payload
2312 .collaborator
2313 .take()
2314 .ok_or_else(|| anyhow!("empty collaborator"))?;
2315
2316 let collaborator = Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
2317 this.update(&mut cx, |this, cx| {
2318 this.collaborators
2319 .insert(collaborator.peer_id, collaborator);
2320 cx.notify();
2321 });
2322
2323 Ok(())
2324 }
2325
2326 async fn handle_remove_collaborator(
2327 this: ModelHandle<Self>,
2328 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
2329 _: Arc<Client>,
2330 mut cx: AsyncAppContext,
2331 ) -> Result<()> {
2332 this.update(&mut cx, |this, cx| {
2333 let peer_id = PeerId(envelope.payload.peer_id);
2334 let replica_id = this
2335 .collaborators
2336 .remove(&peer_id)
2337 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
2338 .replica_id;
2339 this.shared_buffers.remove(&peer_id);
2340 for (_, buffer) in &this.open_buffers {
2341 if let Some(buffer) = buffer.upgrade(cx) {
2342 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
2343 }
2344 }
2345 cx.notify();
2346 Ok(())
2347 })
2348 }
2349
2350 async fn handle_share_worktree(
2351 this: ModelHandle<Self>,
2352 envelope: TypedEnvelope<proto::ShareWorktree>,
2353 client: Arc<Client>,
2354 mut cx: AsyncAppContext,
2355 ) -> Result<()> {
2356 this.update(&mut cx, |this, cx| {
2357 let remote_id = this.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
2358 let replica_id = this.replica_id();
2359 let worktree = envelope
2360 .payload
2361 .worktree
2362 .ok_or_else(|| anyhow!("invalid worktree"))?;
2363 let (worktree, load_task) =
2364 Worktree::remote(remote_id, replica_id, worktree, client, cx);
2365 this.add_worktree(&worktree, cx);
2366 load_task.detach();
2367 Ok(())
2368 })
2369 }
2370
2371 async fn handle_unregister_worktree(
2372 this: ModelHandle<Self>,
2373 envelope: TypedEnvelope<proto::UnregisterWorktree>,
2374 _: Arc<Client>,
2375 mut cx: AsyncAppContext,
2376 ) -> Result<()> {
2377 this.update(&mut cx, |this, cx| {
2378 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
2379 this.remove_worktree(worktree_id, cx);
2380 Ok(())
2381 })
2382 }
2383
2384 async fn handle_update_worktree(
2385 this: ModelHandle<Self>,
2386 envelope: TypedEnvelope<proto::UpdateWorktree>,
2387 _: Arc<Client>,
2388 mut cx: AsyncAppContext,
2389 ) -> Result<()> {
2390 this.update(&mut cx, |this, cx| {
2391 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
2392 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
2393 worktree.update(cx, |worktree, _| {
2394 let worktree = worktree.as_remote_mut().unwrap();
2395 worktree.update_from_remote(envelope)
2396 })?;
2397 }
2398 Ok(())
2399 })
2400 }
2401
2402 async fn handle_update_diagnostic_summary(
2403 this: ModelHandle<Self>,
2404 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
2405 _: Arc<Client>,
2406 mut cx: AsyncAppContext,
2407 ) -> Result<()> {
2408 this.update(&mut cx, |this, cx| {
2409 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
2410 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
2411 if let Some(summary) = envelope.payload.summary {
2412 let project_path = ProjectPath {
2413 worktree_id,
2414 path: Path::new(&summary.path).into(),
2415 };
2416 worktree.update(cx, |worktree, _| {
2417 worktree
2418 .as_remote_mut()
2419 .unwrap()
2420 .update_diagnostic_summary(project_path.path.clone(), &summary);
2421 });
2422 cx.emit(Event::DiagnosticsUpdated(project_path));
2423 }
2424 }
2425 Ok(())
2426 })
2427 }
2428
2429 async fn handle_disk_based_diagnostics_updating(
2430 this: ModelHandle<Self>,
2431 _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
2432 _: Arc<Client>,
2433 mut cx: AsyncAppContext,
2434 ) -> Result<()> {
2435 this.update(&mut cx, |this, cx| this.disk_based_diagnostics_started(cx));
2436 Ok(())
2437 }
2438
2439 async fn handle_disk_based_diagnostics_updated(
2440 this: ModelHandle<Self>,
2441 _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,
2442 _: Arc<Client>,
2443 mut cx: AsyncAppContext,
2444 ) -> Result<()> {
2445 this.update(&mut cx, |this, cx| this.disk_based_diagnostics_finished(cx));
2446 Ok(())
2447 }
2448
2449 async fn handle_update_buffer(
2450 this: ModelHandle<Self>,
2451 envelope: TypedEnvelope<proto::UpdateBuffer>,
2452 _: Arc<Client>,
2453 mut cx: AsyncAppContext,
2454 ) -> Result<()> {
2455 this.update(&mut cx, |this, cx| {
2456 let payload = envelope.payload.clone();
2457 let buffer_id = payload.buffer_id;
2458 let ops = payload
2459 .operations
2460 .into_iter()
2461 .map(|op| language::proto::deserialize_operation(op))
2462 .collect::<Result<Vec<_>, _>>()?;
2463 let is_remote = this.is_remote();
2464 match this.open_buffers.entry(buffer_id) {
2465 hash_map::Entry::Occupied(mut e) => match e.get_mut() {
2466 OpenBuffer::Loaded(buffer) => {
2467 if let Some(buffer) = buffer.upgrade(cx) {
2468 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
2469 }
2470 }
2471 OpenBuffer::Loading(operations) => operations.extend_from_slice(&ops),
2472 },
2473 hash_map::Entry::Vacant(e) => {
2474 if is_remote && this.loading_buffers.len() > 0 {
2475 e.insert(OpenBuffer::Loading(ops));
2476 }
2477 }
2478 }
2479 Ok(())
2480 })
2481 }
2482
2483 async fn handle_update_buffer_file(
2484 this: ModelHandle<Self>,
2485 envelope: TypedEnvelope<proto::UpdateBufferFile>,
2486 _: Arc<Client>,
2487 mut cx: AsyncAppContext,
2488 ) -> Result<()> {
2489 this.update(&mut cx, |this, cx| {
2490 let payload = envelope.payload.clone();
2491 let buffer_id = payload.buffer_id;
2492 let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
2493 let worktree = this
2494 .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
2495 .ok_or_else(|| anyhow!("no such worktree"))?;
2496 let file = File::from_proto(file, worktree.clone(), cx)?;
2497 let buffer = this
2498 .open_buffers
2499 .get_mut(&buffer_id)
2500 .and_then(|b| b.upgrade(cx))
2501 .ok_or_else(|| anyhow!("no such buffer"))?;
2502 buffer.update(cx, |buffer, cx| {
2503 buffer.file_updated(Box::new(file), cx).detach();
2504 });
2505 Ok(())
2506 })
2507 }
2508
2509 async fn handle_save_buffer(
2510 this: ModelHandle<Self>,
2511 envelope: TypedEnvelope<proto::SaveBuffer>,
2512 _: Arc<Client>,
2513 mut cx: AsyncAppContext,
2514 ) -> Result<proto::BufferSaved> {
2515 let buffer_id = envelope.payload.buffer_id;
2516 let sender_id = envelope.original_sender_id()?;
2517 let requested_version = envelope.payload.version.try_into()?;
2518
2519 let (project_id, buffer) = this.update(&mut cx, |this, _| {
2520 let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
2521 let buffer = this
2522 .shared_buffers
2523 .get(&sender_id)
2524 .and_then(|shared_buffers| shared_buffers.get(&buffer_id).cloned())
2525 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
2526 Ok::<_, anyhow::Error>((project_id, buffer))
2527 })?;
2528
2529 if !buffer
2530 .read_with(&cx, |buffer, _| buffer.version())
2531 .observed_all(&requested_version)
2532 {
2533 Err(anyhow!("save request depends on unreceived edits"))?;
2534 }
2535
2536 let (saved_version, mtime) = buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?;
2537 Ok(proto::BufferSaved {
2538 project_id,
2539 buffer_id,
2540 version: (&saved_version).into(),
2541 mtime: Some(mtime.into()),
2542 })
2543 }
2544
2545 async fn handle_format_buffers(
2546 this: ModelHandle<Self>,
2547 envelope: TypedEnvelope<proto::FormatBuffers>,
2548 _: Arc<Client>,
2549 mut cx: AsyncAppContext,
2550 ) -> Result<proto::FormatBuffersResponse> {
2551 let sender_id = envelope.original_sender_id()?;
2552 let format = this.update(&mut cx, |this, cx| {
2553 let shared_buffers = this
2554 .shared_buffers
2555 .get(&sender_id)
2556 .ok_or_else(|| anyhow!("peer has no buffers"))?;
2557 let mut buffers = HashSet::default();
2558 for buffer_id in &envelope.payload.buffer_ids {
2559 buffers.insert(
2560 shared_buffers
2561 .get(buffer_id)
2562 .cloned()
2563 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
2564 );
2565 }
2566 Ok::<_, anyhow::Error>(this.format(buffers, false, cx))
2567 })?;
2568
2569 let project_transaction = format.await?;
2570 let project_transaction = this.update(&mut cx, |this, cx| {
2571 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
2572 });
2573 Ok(proto::FormatBuffersResponse {
2574 transaction: Some(project_transaction),
2575 })
2576 }
2577
2578 async fn handle_get_completions(
2579 this: ModelHandle<Self>,
2580 envelope: TypedEnvelope<proto::GetCompletions>,
2581 _: Arc<Client>,
2582 mut cx: AsyncAppContext,
2583 ) -> Result<proto::GetCompletionsResponse> {
2584 let sender_id = envelope.original_sender_id()?;
2585 let position = envelope
2586 .payload
2587 .position
2588 .and_then(language::proto::deserialize_anchor)
2589 .ok_or_else(|| anyhow!("invalid position"))?;
2590 let version = clock::Global::from(envelope.payload.version);
2591 let buffer = this.read_with(&cx, |this, _| {
2592 this.shared_buffers
2593 .get(&sender_id)
2594 .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
2595 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
2596 })?;
2597 if !buffer
2598 .read_with(&cx, |buffer, _| buffer.version())
2599 .observed_all(&version)
2600 {
2601 Err(anyhow!("completion request depends on unreceived edits"))?;
2602 }
2603 let version = buffer.read_with(&cx, |buffer, _| buffer.version());
2604 let completions = this
2605 .update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
2606 .await?;
2607
2608 Ok(proto::GetCompletionsResponse {
2609 completions: completions
2610 .iter()
2611 .map(language::proto::serialize_completion)
2612 .collect(),
2613 version: (&version).into(),
2614 })
2615 }
2616
2617 async fn handle_apply_additional_edits_for_completion(
2618 this: ModelHandle<Self>,
2619 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
2620 _: Arc<Client>,
2621 mut cx: AsyncAppContext,
2622 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
2623 let sender_id = envelope.original_sender_id()?;
2624 let apply_additional_edits = this.update(&mut cx, |this, cx| {
2625 let buffer = this
2626 .shared_buffers
2627 .get(&sender_id)
2628 .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
2629 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
2630 let language = buffer.read(cx).language();
2631 let completion = language::proto::deserialize_completion(
2632 envelope
2633 .payload
2634 .completion
2635 .ok_or_else(|| anyhow!("invalid completion"))?,
2636 language,
2637 )?;
2638 Ok::<_, anyhow::Error>(
2639 this.apply_additional_edits_for_completion(buffer, completion, false, cx),
2640 )
2641 })?;
2642
2643 Ok(proto::ApplyCompletionAdditionalEditsResponse {
2644 transaction: apply_additional_edits
2645 .await?
2646 .as_ref()
2647 .map(language::proto::serialize_transaction),
2648 })
2649 }
2650
2651 async fn handle_get_code_actions(
2652 this: ModelHandle<Self>,
2653 envelope: TypedEnvelope<proto::GetCodeActions>,
2654 _: Arc<Client>,
2655 mut cx: AsyncAppContext,
2656 ) -> Result<proto::GetCodeActionsResponse> {
2657 let sender_id = envelope.original_sender_id()?;
2658 let start = envelope
2659 .payload
2660 .start
2661 .and_then(language::proto::deserialize_anchor)
2662 .ok_or_else(|| anyhow!("invalid start"))?;
2663 let end = envelope
2664 .payload
2665 .end
2666 .and_then(language::proto::deserialize_anchor)
2667 .ok_or_else(|| anyhow!("invalid end"))?;
2668 let buffer = this.update(&mut cx, |this, _| {
2669 this.shared_buffers
2670 .get(&sender_id)
2671 .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
2672 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
2673 })?;
2674 let version = buffer.read_with(&cx, |buffer, _| buffer.version());
2675 if !version.observed(start.timestamp) || !version.observed(end.timestamp) {
2676 Err(anyhow!("code action request references unreceived edits"))?;
2677 }
2678 let code_actions = this.update(&mut cx, |this, cx| {
2679 Ok::<_, anyhow::Error>(this.code_actions(&buffer, start..end, cx))
2680 })?;
2681
2682 Ok(proto::GetCodeActionsResponse {
2683 actions: code_actions
2684 .await?
2685 .iter()
2686 .map(language::proto::serialize_code_action)
2687 .collect(),
2688 version: (&version).into(),
2689 })
2690 }
2691
2692 async fn handle_apply_code_action(
2693 this: ModelHandle<Self>,
2694 envelope: TypedEnvelope<proto::ApplyCodeAction>,
2695 _: Arc<Client>,
2696 mut cx: AsyncAppContext,
2697 ) -> Result<proto::ApplyCodeActionResponse> {
2698 let sender_id = envelope.original_sender_id()?;
2699 let action = language::proto::deserialize_code_action(
2700 envelope
2701 .payload
2702 .action
2703 .ok_or_else(|| anyhow!("invalid action"))?,
2704 )?;
2705 let apply_code_action = this.update(&mut cx, |this, cx| {
2706 let buffer = this
2707 .shared_buffers
2708 .get(&sender_id)
2709 .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
2710 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
2711 Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
2712 })?;
2713
2714 let project_transaction = apply_code_action.await?;
2715 let project_transaction = this.update(&mut cx, |this, cx| {
2716 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
2717 });
2718 Ok(proto::ApplyCodeActionResponse {
2719 transaction: Some(project_transaction),
2720 })
2721 }
2722
2723 async fn handle_lsp_command<T: LspCommand>(
2724 this: ModelHandle<Self>,
2725 envelope: TypedEnvelope<T::ProtoRequest>,
2726 _: Arc<Client>,
2727 mut cx: AsyncAppContext,
2728 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
2729 where
2730 <T::LspRequest as lsp::request::Request>::Result: Send,
2731 {
2732 let sender_id = envelope.original_sender_id()?;
2733 let (request, buffer_version) = this.update(&mut cx, |this, cx| {
2734 let buffer_id = T::buffer_id_from_proto(&envelope.payload);
2735 let buffer_handle = this
2736 .shared_buffers
2737 .get(&sender_id)
2738 .and_then(|shared_buffers| shared_buffers.get(&buffer_id).cloned())
2739 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
2740 let buffer = buffer_handle.read(cx);
2741 let buffer_version = buffer.version();
2742 let request = T::from_proto(envelope.payload, this, buffer)?;
2743 Ok::<_, anyhow::Error>((this.request_lsp(buffer_handle, request, cx), buffer_version))
2744 })?;
2745 let response = request.await?;
2746 this.update(&mut cx, |this, cx| {
2747 Ok(T::response_to_proto(
2748 response,
2749 this,
2750 sender_id,
2751 &buffer_version,
2752 cx,
2753 ))
2754 })
2755 }
2756
2757 async fn handle_get_project_symbols(
2758 this: ModelHandle<Self>,
2759 envelope: TypedEnvelope<proto::GetProjectSymbols>,
2760 _: Arc<Client>,
2761 mut cx: AsyncAppContext,
2762 ) -> Result<proto::GetProjectSymbolsResponse> {
2763 let symbols = this
2764 .update(&mut cx, |this, cx| {
2765 this.symbols(&envelope.payload.query, cx)
2766 })
2767 .await?;
2768
2769 Ok(proto::GetProjectSymbolsResponse {
2770 symbols: symbols.iter().map(serialize_symbol).collect(),
2771 })
2772 }
2773
2774 async fn handle_open_buffer_for_symbol(
2775 this: ModelHandle<Self>,
2776 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
2777 _: Arc<Client>,
2778 mut cx: AsyncAppContext,
2779 ) -> Result<proto::OpenBufferForSymbolResponse> {
2780 let peer_id = envelope.original_sender_id()?;
2781 let symbol = envelope
2782 .payload
2783 .symbol
2784 .ok_or_else(|| anyhow!("invalid symbol"))?;
2785 let symbol = this.read_with(&cx, |this, _| {
2786 let symbol = this.deserialize_symbol(symbol)?;
2787 let signature = this.symbol_signature(symbol.worktree_id, &symbol.path);
2788 if signature == symbol.signature {
2789 Ok(symbol)
2790 } else {
2791 Err(anyhow!("invalid symbol signature"))
2792 }
2793 })?;
2794 let buffer = this
2795 .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))
2796 .await?;
2797
2798 Ok(proto::OpenBufferForSymbolResponse {
2799 buffer: Some(this.update(&mut cx, |this, cx| {
2800 this.serialize_buffer_for_peer(&buffer, peer_id, cx)
2801 })),
2802 })
2803 }
2804
2805 fn symbol_signature(&self, worktree_id: WorktreeId, path: &Path) -> [u8; 32] {
2806 let mut hasher = Sha256::new();
2807 hasher.update(worktree_id.to_proto().to_be_bytes());
2808 hasher.update(path.to_string_lossy().as_bytes());
2809 hasher.update(self.nonce.to_be_bytes());
2810 hasher.finalize().as_slice().try_into().unwrap()
2811 }
2812
2813 async fn handle_open_buffer(
2814 this: ModelHandle<Self>,
2815 envelope: TypedEnvelope<proto::OpenBuffer>,
2816 _: Arc<Client>,
2817 mut cx: AsyncAppContext,
2818 ) -> Result<proto::OpenBufferResponse> {
2819 let peer_id = envelope.original_sender_id()?;
2820 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
2821 let open_buffer = this.update(&mut cx, |this, cx| {
2822 this.open_buffer(
2823 ProjectPath {
2824 worktree_id,
2825 path: PathBuf::from(envelope.payload.path).into(),
2826 },
2827 cx,
2828 )
2829 });
2830
2831 let buffer = open_buffer.await?;
2832 this.update(&mut cx, |this, cx| {
2833 Ok(proto::OpenBufferResponse {
2834 buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
2835 })
2836 })
2837 }
2838
2839 fn serialize_project_transaction_for_peer(
2840 &mut self,
2841 project_transaction: ProjectTransaction,
2842 peer_id: PeerId,
2843 cx: &AppContext,
2844 ) -> proto::ProjectTransaction {
2845 let mut serialized_transaction = proto::ProjectTransaction {
2846 buffers: Default::default(),
2847 transactions: Default::default(),
2848 };
2849 for (buffer, transaction) in project_transaction.0 {
2850 serialized_transaction
2851 .buffers
2852 .push(self.serialize_buffer_for_peer(&buffer, peer_id, cx));
2853 serialized_transaction
2854 .transactions
2855 .push(language::proto::serialize_transaction(&transaction));
2856 }
2857 serialized_transaction
2858 }
2859
2860 fn deserialize_project_transaction(
2861 &mut self,
2862 message: proto::ProjectTransaction,
2863 push_to_history: bool,
2864 cx: &mut ModelContext<Self>,
2865 ) -> Task<Result<ProjectTransaction>> {
2866 cx.spawn(|this, mut cx| async move {
2867 let mut project_transaction = ProjectTransaction::default();
2868 for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
2869 let buffer = this
2870 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
2871 .await?;
2872 let transaction = language::proto::deserialize_transaction(transaction)?;
2873 project_transaction.0.insert(buffer, transaction);
2874 }
2875 for (buffer, transaction) in &project_transaction.0 {
2876 buffer
2877 .update(&mut cx, |buffer, _| {
2878 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
2879 })
2880 .await;
2881
2882 if push_to_history {
2883 buffer.update(&mut cx, |buffer, _| {
2884 buffer.push_transaction(transaction.clone(), Instant::now());
2885 });
2886 }
2887 }
2888
2889 Ok(project_transaction)
2890 })
2891 }
2892
2893 fn serialize_buffer_for_peer(
2894 &mut self,
2895 buffer: &ModelHandle<Buffer>,
2896 peer_id: PeerId,
2897 cx: &AppContext,
2898 ) -> proto::Buffer {
2899 let buffer_id = buffer.read(cx).remote_id();
2900 let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
2901 match shared_buffers.entry(buffer_id) {
2902 hash_map::Entry::Occupied(_) => proto::Buffer {
2903 variant: Some(proto::buffer::Variant::Id(buffer_id)),
2904 },
2905 hash_map::Entry::Vacant(entry) => {
2906 entry.insert(buffer.clone());
2907 proto::Buffer {
2908 variant: Some(proto::buffer::Variant::State(buffer.read(cx).to_proto())),
2909 }
2910 }
2911 }
2912 }
2913
2914 fn deserialize_buffer(
2915 &mut self,
2916 buffer: proto::Buffer,
2917 cx: &mut ModelContext<Self>,
2918 ) -> Task<Result<ModelHandle<Buffer>>> {
2919 let replica_id = self.replica_id();
2920
2921 let mut opened_buffer_tx = self.opened_buffer.clone();
2922 let mut opened_buffer_rx = self.opened_buffer.subscribe();
2923 cx.spawn(|this, mut cx| async move {
2924 match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
2925 proto::buffer::Variant::Id(id) => {
2926 let buffer = loop {
2927 let buffer = this.read_with(&cx, |this, cx| {
2928 this.open_buffers
2929 .get(&id)
2930 .and_then(|buffer| buffer.upgrade(cx))
2931 });
2932 if let Some(buffer) = buffer {
2933 break buffer;
2934 }
2935 opened_buffer_rx
2936 .recv()
2937 .await
2938 .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
2939 };
2940 Ok(buffer)
2941 }
2942 proto::buffer::Variant::State(mut buffer) => {
2943 let mut buffer_worktree = None;
2944 let mut buffer_file = None;
2945 if let Some(file) = buffer.file.take() {
2946 this.read_with(&cx, |this, cx| {
2947 let worktree_id = WorktreeId::from_proto(file.worktree_id);
2948 let worktree =
2949 this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
2950 anyhow!("no worktree found for id {}", file.worktree_id)
2951 })?;
2952 buffer_file =
2953 Some(Box::new(File::from_proto(file, worktree.clone(), cx)?)
2954 as Box<dyn language::File>);
2955 buffer_worktree = Some(worktree);
2956 Ok::<_, anyhow::Error>(())
2957 })?;
2958 }
2959
2960 let buffer = cx.add_model(|cx| {
2961 Buffer::from_proto(replica_id, buffer, buffer_file, cx).unwrap()
2962 });
2963 this.update(&mut cx, |this, cx| {
2964 this.register_buffer(&buffer, buffer_worktree.as_ref(), cx)
2965 })?;
2966
2967 let _ = opened_buffer_tx.send(()).await;
2968 Ok(buffer)
2969 }
2970 }
2971 })
2972 }
2973
2974 fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result<Symbol> {
2975 let language = self
2976 .languages
2977 .get_language(&serialized_symbol.language_name);
2978 let start = serialized_symbol
2979 .start
2980 .ok_or_else(|| anyhow!("invalid start"))?;
2981 let end = serialized_symbol
2982 .end
2983 .ok_or_else(|| anyhow!("invalid end"))?;
2984 let kind = unsafe { mem::transmute(serialized_symbol.kind) };
2985 Ok(Symbol {
2986 source_worktree_id: WorktreeId::from_proto(serialized_symbol.source_worktree_id),
2987 worktree_id: WorktreeId::from_proto(serialized_symbol.worktree_id),
2988 language_name: serialized_symbol.language_name.clone(),
2989 label: language
2990 .and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind))
2991 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)),
2992 name: serialized_symbol.name,
2993 path: PathBuf::from(serialized_symbol.path),
2994 range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column),
2995 kind,
2996 signature: serialized_symbol
2997 .signature
2998 .try_into()
2999 .map_err(|_| anyhow!("invalid signature"))?,
3000 })
3001 }
3002
3003 async fn handle_close_buffer(
3004 this: ModelHandle<Self>,
3005 envelope: TypedEnvelope<proto::CloseBuffer>,
3006 _: Arc<Client>,
3007 mut cx: AsyncAppContext,
3008 ) -> Result<()> {
3009 this.update(&mut cx, |this, cx| {
3010 if let Some(shared_buffers) =
3011 this.shared_buffers.get_mut(&envelope.original_sender_id()?)
3012 {
3013 shared_buffers.remove(&envelope.payload.buffer_id);
3014 cx.notify();
3015 }
3016 Ok(())
3017 })
3018 }
3019
3020 async fn handle_buffer_saved(
3021 this: ModelHandle<Self>,
3022 envelope: TypedEnvelope<proto::BufferSaved>,
3023 _: Arc<Client>,
3024 mut cx: AsyncAppContext,
3025 ) -> Result<()> {
3026 let version = envelope.payload.version.try_into()?;
3027 let mtime = envelope
3028 .payload
3029 .mtime
3030 .ok_or_else(|| anyhow!("missing mtime"))?
3031 .into();
3032
3033 this.update(&mut cx, |this, cx| {
3034 let buffer = this
3035 .open_buffers
3036 .get(&envelope.payload.buffer_id)
3037 .and_then(|buffer| buffer.upgrade(cx));
3038 if let Some(buffer) = buffer {
3039 buffer.update(cx, |buffer, cx| {
3040 buffer.did_save(version, mtime, None, cx);
3041 });
3042 }
3043 Ok(())
3044 })
3045 }
3046
3047 async fn handle_buffer_reloaded(
3048 this: ModelHandle<Self>,
3049 envelope: TypedEnvelope<proto::BufferReloaded>,
3050 _: Arc<Client>,
3051 mut cx: AsyncAppContext,
3052 ) -> Result<()> {
3053 let payload = envelope.payload.clone();
3054 let version = payload.version.try_into()?;
3055 let mtime = payload
3056 .mtime
3057 .ok_or_else(|| anyhow!("missing mtime"))?
3058 .into();
3059 this.update(&mut cx, |this, cx| {
3060 let buffer = this
3061 .open_buffers
3062 .get(&payload.buffer_id)
3063 .and_then(|buffer| buffer.upgrade(cx));
3064 if let Some(buffer) = buffer {
3065 buffer.update(cx, |buffer, cx| {
3066 buffer.did_reload(version, mtime, cx);
3067 });
3068 }
3069 Ok(())
3070 })
3071 }
3072
3073 pub fn match_paths<'a>(
3074 &self,
3075 query: &'a str,
3076 include_ignored: bool,
3077 smart_case: bool,
3078 max_results: usize,
3079 cancel_flag: &'a AtomicBool,
3080 cx: &AppContext,
3081 ) -> impl 'a + Future<Output = Vec<PathMatch>> {
3082 let worktrees = self
3083 .worktrees(cx)
3084 .filter(|worktree| !worktree.read(cx).is_weak())
3085 .collect::<Vec<_>>();
3086 let include_root_name = worktrees.len() > 1;
3087 let candidate_sets = worktrees
3088 .into_iter()
3089 .map(|worktree| CandidateSet {
3090 snapshot: worktree.read(cx).snapshot(),
3091 include_ignored,
3092 include_root_name,
3093 })
3094 .collect::<Vec<_>>();
3095
3096 let background = cx.background().clone();
3097 async move {
3098 fuzzy::match_paths(
3099 candidate_sets.as_slice(),
3100 query,
3101 smart_case,
3102 max_results,
3103 cancel_flag,
3104 background,
3105 )
3106 .await
3107 }
3108 }
3109}
3110
3111impl WorktreeHandle {
3112 pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
3113 match self {
3114 WorktreeHandle::Strong(handle) => Some(handle.clone()),
3115 WorktreeHandle::Weak(handle) => handle.upgrade(cx),
3116 }
3117 }
3118}
3119
3120impl OpenBuffer {
3121 pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
3122 match self {
3123 OpenBuffer::Loaded(handle) => handle.upgrade(cx),
3124 OpenBuffer::Loading(_) => None,
3125 }
3126 }
3127}
3128
3129struct CandidateSet {
3130 snapshot: Snapshot,
3131 include_ignored: bool,
3132 include_root_name: bool,
3133}
3134
3135impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
3136 type Candidates = CandidateSetIter<'a>;
3137
3138 fn id(&self) -> usize {
3139 self.snapshot.id().to_usize()
3140 }
3141
3142 fn len(&self) -> usize {
3143 if self.include_ignored {
3144 self.snapshot.file_count()
3145 } else {
3146 self.snapshot.visible_file_count()
3147 }
3148 }
3149
3150 fn prefix(&self) -> Arc<str> {
3151 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
3152 self.snapshot.root_name().into()
3153 } else if self.include_root_name {
3154 format!("{}/", self.snapshot.root_name()).into()
3155 } else {
3156 "".into()
3157 }
3158 }
3159
3160 fn candidates(&'a self, start: usize) -> Self::Candidates {
3161 CandidateSetIter {
3162 traversal: self.snapshot.files(self.include_ignored, start),
3163 }
3164 }
3165}
3166
3167struct CandidateSetIter<'a> {
3168 traversal: Traversal<'a>,
3169}
3170
3171impl<'a> Iterator for CandidateSetIter<'a> {
3172 type Item = PathMatchCandidate<'a>;
3173
3174 fn next(&mut self) -> Option<Self::Item> {
3175 self.traversal.next().map(|entry| {
3176 if let EntryKind::File(char_bag) = entry.kind {
3177 PathMatchCandidate {
3178 path: &entry.path,
3179 char_bag,
3180 }
3181 } else {
3182 unreachable!()
3183 }
3184 })
3185 }
3186}
3187
3188impl Entity for Project {
3189 type Event = Event;
3190
3191 fn release(&mut self, _: &mut gpui::MutableAppContext) {
3192 match &self.client_state {
3193 ProjectClientState::Local { remote_id_rx, .. } => {
3194 if let Some(project_id) = *remote_id_rx.borrow() {
3195 self.client
3196 .send(proto::UnregisterProject { project_id })
3197 .log_err();
3198 }
3199 }
3200 ProjectClientState::Remote { remote_id, .. } => {
3201 self.client
3202 .send(proto::LeaveProject {
3203 project_id: *remote_id,
3204 })
3205 .log_err();
3206 }
3207 }
3208 }
3209
3210 fn app_will_quit(
3211 &mut self,
3212 _: &mut MutableAppContext,
3213 ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
3214 let shutdown_futures = self
3215 .language_servers
3216 .drain()
3217 .filter_map(|(_, server)| server.shutdown())
3218 .collect::<Vec<_>>();
3219 Some(
3220 async move {
3221 futures::future::join_all(shutdown_futures).await;
3222 }
3223 .boxed(),
3224 )
3225 }
3226}
3227
3228impl Collaborator {
3229 fn from_proto(
3230 message: proto::Collaborator,
3231 user_store: &ModelHandle<UserStore>,
3232 cx: &mut AsyncAppContext,
3233 ) -> impl Future<Output = Result<Self>> {
3234 let user = user_store.update(cx, |user_store, cx| {
3235 user_store.fetch_user(message.user_id, cx)
3236 });
3237
3238 async move {
3239 Ok(Self {
3240 peer_id: PeerId(message.peer_id),
3241 user: user.await?,
3242 replica_id: message.replica_id as ReplicaId,
3243 })
3244 }
3245 }
3246}
3247
3248impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
3249 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
3250 Self {
3251 worktree_id,
3252 path: path.as_ref().into(),
3253 }
3254 }
3255}
3256
3257impl From<lsp::CreateFileOptions> for fs::CreateOptions {
3258 fn from(options: lsp::CreateFileOptions) -> Self {
3259 Self {
3260 overwrite: options.overwrite.unwrap_or(false),
3261 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
3262 }
3263 }
3264}
3265
3266impl From<lsp::RenameFileOptions> for fs::RenameOptions {
3267 fn from(options: lsp::RenameFileOptions) -> Self {
3268 Self {
3269 overwrite: options.overwrite.unwrap_or(false),
3270 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
3271 }
3272 }
3273}
3274
3275impl From<lsp::DeleteFileOptions> for fs::RemoveOptions {
3276 fn from(options: lsp::DeleteFileOptions) -> Self {
3277 Self {
3278 recursive: options.recursive.unwrap_or(false),
3279 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
3280 }
3281 }
3282}
3283
3284fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
3285 proto::Symbol {
3286 source_worktree_id: symbol.source_worktree_id.to_proto(),
3287 worktree_id: symbol.worktree_id.to_proto(),
3288 language_name: symbol.language_name.clone(),
3289 name: symbol.name.clone(),
3290 kind: unsafe { mem::transmute(symbol.kind) },
3291 path: symbol.path.to_string_lossy().to_string(),
3292 start: Some(proto::Point {
3293 row: symbol.range.start.row,
3294 column: symbol.range.start.column,
3295 }),
3296 end: Some(proto::Point {
3297 row: symbol.range.end.row,
3298 column: symbol.range.end.column,
3299 }),
3300 signature: symbol.signature.to_vec(),
3301 }
3302}
3303
3304fn relativize_path(base: &Path, path: &Path) -> PathBuf {
3305 let mut path_components = path.components();
3306 let mut base_components = base.components();
3307 let mut components: Vec<Component> = Vec::new();
3308 loop {
3309 match (path_components.next(), base_components.next()) {
3310 (None, None) => break,
3311 (Some(a), None) => {
3312 components.push(a);
3313 components.extend(path_components.by_ref());
3314 break;
3315 }
3316 (None, _) => components.push(Component::ParentDir),
3317 (Some(a), Some(b)) if components.is_empty() && a == b => (),
3318 (Some(a), Some(b)) if b == Component::CurDir => components.push(a),
3319 (Some(a), Some(_)) => {
3320 components.push(Component::ParentDir);
3321 for _ in base_components {
3322 components.push(Component::ParentDir);
3323 }
3324 components.push(a);
3325 components.extend(path_components.by_ref());
3326 break;
3327 }
3328 }
3329 }
3330 components.iter().map(|c| c.as_os_str()).collect()
3331}
3332
3333#[cfg(test)]
3334mod tests {
3335 use super::{Event, *};
3336 use fs::RealFs;
3337 use futures::StreamExt;
3338 use gpui::test::subscribe;
3339 use language::{
3340 tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageServerConfig, Point,
3341 };
3342 use lsp::Url;
3343 use serde_json::json;
3344 use std::{cell::RefCell, os::unix, path::PathBuf, rc::Rc};
3345 use unindent::Unindent as _;
3346 use util::test::temp_tree;
3347 use worktree::WorktreeHandle as _;
3348
3349 #[gpui::test]
3350 async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
3351 let dir = temp_tree(json!({
3352 "root": {
3353 "apple": "",
3354 "banana": {
3355 "carrot": {
3356 "date": "",
3357 "endive": "",
3358 }
3359 },
3360 "fennel": {
3361 "grape": "",
3362 }
3363 }
3364 }));
3365
3366 let root_link_path = dir.path().join("root_link");
3367 unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
3368 unix::fs::symlink(
3369 &dir.path().join("root/fennel"),
3370 &dir.path().join("root/finnochio"),
3371 )
3372 .unwrap();
3373
3374 let project = Project::test(Arc::new(RealFs), &mut cx);
3375
3376 let (tree, _) = project
3377 .update(&mut cx, |project, cx| {
3378 project.find_or_create_local_worktree(&root_link_path, false, cx)
3379 })
3380 .await
3381 .unwrap();
3382
3383 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3384 .await;
3385 cx.read(|cx| {
3386 let tree = tree.read(cx);
3387 assert_eq!(tree.file_count(), 5);
3388 assert_eq!(
3389 tree.inode_for_path("fennel/grape"),
3390 tree.inode_for_path("finnochio/grape")
3391 );
3392 });
3393
3394 let cancel_flag = Default::default();
3395 let results = project
3396 .read_with(&cx, |project, cx| {
3397 project.match_paths("bna", false, false, 10, &cancel_flag, cx)
3398 })
3399 .await;
3400 assert_eq!(
3401 results
3402 .into_iter()
3403 .map(|result| result.path)
3404 .collect::<Vec<Arc<Path>>>(),
3405 vec![
3406 PathBuf::from("banana/carrot/date").into(),
3407 PathBuf::from("banana/carrot/endive").into(),
3408 ]
3409 );
3410 }
3411
3412 #[gpui::test]
3413 async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
3414 let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
3415 let progress_token = language_server_config
3416 .disk_based_diagnostics_progress_token
3417 .clone()
3418 .unwrap();
3419
3420 let language = Arc::new(Language::new(
3421 LanguageConfig {
3422 name: "Rust".into(),
3423 path_suffixes: vec!["rs".to_string()],
3424 language_server: Some(language_server_config),
3425 ..Default::default()
3426 },
3427 Some(tree_sitter_rust::language()),
3428 ));
3429
3430 let fs = FakeFs::new(cx.background());
3431 fs.insert_tree(
3432 "/dir",
3433 json!({
3434 "a.rs": "fn a() { A }",
3435 "b.rs": "const y: i32 = 1",
3436 }),
3437 )
3438 .await;
3439
3440 let project = Project::test(fs, &mut cx);
3441 project.update(&mut cx, |project, _| {
3442 Arc::get_mut(&mut project.languages).unwrap().add(language);
3443 });
3444
3445 let (tree, _) = project
3446 .update(&mut cx, |project, cx| {
3447 project.find_or_create_local_worktree("/dir", false, cx)
3448 })
3449 .await
3450 .unwrap();
3451 let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
3452
3453 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3454 .await;
3455
3456 // Cause worktree to start the fake language server
3457 let _buffer = project
3458 .update(&mut cx, |project, cx| {
3459 project.open_buffer((worktree_id, Path::new("b.rs")), cx)
3460 })
3461 .await
3462 .unwrap();
3463
3464 let mut events = subscribe(&project, &mut cx);
3465
3466 let mut fake_server = fake_servers.next().await.unwrap();
3467 fake_server.start_progress(&progress_token).await;
3468 assert_eq!(
3469 events.next().await.unwrap(),
3470 Event::DiskBasedDiagnosticsStarted
3471 );
3472
3473 fake_server.start_progress(&progress_token).await;
3474 fake_server.end_progress(&progress_token).await;
3475 fake_server.start_progress(&progress_token).await;
3476
3477 fake_server
3478 .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
3479 uri: Url::from_file_path("/dir/a.rs").unwrap(),
3480 version: None,
3481 diagnostics: vec![lsp::Diagnostic {
3482 range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
3483 severity: Some(lsp::DiagnosticSeverity::ERROR),
3484 message: "undefined variable 'A'".to_string(),
3485 ..Default::default()
3486 }],
3487 })
3488 .await;
3489 assert_eq!(
3490 events.next().await.unwrap(),
3491 Event::DiagnosticsUpdated((worktree_id, Path::new("a.rs")).into())
3492 );
3493
3494 fake_server.end_progress(&progress_token).await;
3495 fake_server.end_progress(&progress_token).await;
3496 assert_eq!(
3497 events.next().await.unwrap(),
3498 Event::DiskBasedDiagnosticsUpdated
3499 );
3500 assert_eq!(
3501 events.next().await.unwrap(),
3502 Event::DiskBasedDiagnosticsFinished
3503 );
3504
3505 let buffer = project
3506 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
3507 .await
3508 .unwrap();
3509
3510 buffer.read_with(&cx, |buffer, _| {
3511 let snapshot = buffer.snapshot();
3512 let diagnostics = snapshot
3513 .diagnostics_in_range::<_, Point>(0..buffer.len())
3514 .collect::<Vec<_>>();
3515 assert_eq!(
3516 diagnostics,
3517 &[DiagnosticEntry {
3518 range: Point::new(0, 9)..Point::new(0, 10),
3519 diagnostic: Diagnostic {
3520 severity: lsp::DiagnosticSeverity::ERROR,
3521 message: "undefined variable 'A'".to_string(),
3522 group_id: 0,
3523 is_primary: true,
3524 ..Default::default()
3525 }
3526 }]
3527 )
3528 });
3529 }
3530
3531 #[gpui::test]
3532 async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
3533 let dir = temp_tree(json!({
3534 "root": {
3535 "dir1": {},
3536 "dir2": {
3537 "dir3": {}
3538 }
3539 }
3540 }));
3541
3542 let project = Project::test(Arc::new(RealFs), &mut cx);
3543 let (tree, _) = project
3544 .update(&mut cx, |project, cx| {
3545 project.find_or_create_local_worktree(&dir.path(), false, cx)
3546 })
3547 .await
3548 .unwrap();
3549
3550 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3551 .await;
3552
3553 let cancel_flag = Default::default();
3554 let results = project
3555 .read_with(&cx, |project, cx| {
3556 project.match_paths("dir", false, false, 10, &cancel_flag, cx)
3557 })
3558 .await;
3559
3560 assert!(results.is_empty());
3561 }
3562
3563 #[gpui::test]
3564 async fn test_definition(mut cx: gpui::TestAppContext) {
3565 let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
3566 let language = Arc::new(Language::new(
3567 LanguageConfig {
3568 name: "Rust".into(),
3569 path_suffixes: vec!["rs".to_string()],
3570 language_server: Some(language_server_config),
3571 ..Default::default()
3572 },
3573 Some(tree_sitter_rust::language()),
3574 ));
3575
3576 let fs = FakeFs::new(cx.background());
3577 fs.insert_tree(
3578 "/dir",
3579 json!({
3580 "a.rs": "const fn a() { A }",
3581 "b.rs": "const y: i32 = crate::a()",
3582 }),
3583 )
3584 .await;
3585
3586 let project = Project::test(fs, &mut cx);
3587 project.update(&mut cx, |project, _| {
3588 Arc::get_mut(&mut project.languages).unwrap().add(language);
3589 });
3590
3591 let (tree, _) = project
3592 .update(&mut cx, |project, cx| {
3593 project.find_or_create_local_worktree("/dir/b.rs", false, cx)
3594 })
3595 .await
3596 .unwrap();
3597 let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
3598 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3599 .await;
3600
3601 let buffer = project
3602 .update(&mut cx, |project, cx| {
3603 project.open_buffer(
3604 ProjectPath {
3605 worktree_id,
3606 path: Path::new("").into(),
3607 },
3608 cx,
3609 )
3610 })
3611 .await
3612 .unwrap();
3613
3614 let mut fake_server = fake_servers.next().await.unwrap();
3615 fake_server.handle_request::<lsp::request::GotoDefinition, _>(move |params, _| {
3616 let params = params.text_document_position_params;
3617 assert_eq!(
3618 params.text_document.uri.to_file_path().unwrap(),
3619 Path::new("/dir/b.rs"),
3620 );
3621 assert_eq!(params.position, lsp::Position::new(0, 22));
3622
3623 Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new(
3624 lsp::Url::from_file_path("/dir/a.rs").unwrap(),
3625 lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
3626 )))
3627 });
3628
3629 let mut definitions = project
3630 .update(&mut cx, |project, cx| project.definition(&buffer, 22, cx))
3631 .await
3632 .unwrap();
3633
3634 assert_eq!(definitions.len(), 1);
3635 let definition = definitions.pop().unwrap();
3636 cx.update(|cx| {
3637 let target_buffer = definition.buffer.read(cx);
3638 assert_eq!(
3639 target_buffer
3640 .file()
3641 .unwrap()
3642 .as_local()
3643 .unwrap()
3644 .abs_path(cx),
3645 Path::new("/dir/a.rs"),
3646 );
3647 assert_eq!(definition.range.to_offset(target_buffer), 9..10);
3648 assert_eq!(
3649 list_worktrees(&project, cx),
3650 [("/dir/b.rs".as_ref(), false), ("/dir/a.rs".as_ref(), true)]
3651 );
3652
3653 drop(definition);
3654 });
3655 cx.read(|cx| {
3656 assert_eq!(
3657 list_worktrees(&project, cx),
3658 [("/dir/b.rs".as_ref(), false)]
3659 );
3660 });
3661
3662 fn list_worktrees<'a>(
3663 project: &'a ModelHandle<Project>,
3664 cx: &'a AppContext,
3665 ) -> Vec<(&'a Path, bool)> {
3666 project
3667 .read(cx)
3668 .worktrees(cx)
3669 .map(|worktree| {
3670 let worktree = worktree.read(cx);
3671 (
3672 worktree.as_local().unwrap().abs_path().as_ref(),
3673 worktree.is_weak(),
3674 )
3675 })
3676 .collect::<Vec<_>>()
3677 }
3678 }
3679
3680 #[gpui::test]
3681 async fn test_save_file(mut cx: gpui::TestAppContext) {
3682 let fs = FakeFs::new(cx.background());
3683 fs.insert_tree(
3684 "/dir",
3685 json!({
3686 "file1": "the old contents",
3687 }),
3688 )
3689 .await;
3690
3691 let project = Project::test(fs.clone(), &mut cx);
3692 let worktree_id = project
3693 .update(&mut cx, |p, cx| {
3694 p.find_or_create_local_worktree("/dir", false, cx)
3695 })
3696 .await
3697 .unwrap()
3698 .0
3699 .read_with(&cx, |tree, _| tree.id());
3700
3701 let buffer = project
3702 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
3703 .await
3704 .unwrap();
3705 buffer
3706 .update(&mut cx, |buffer, cx| {
3707 assert_eq!(buffer.text(), "the old contents");
3708 buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
3709 buffer.save(cx)
3710 })
3711 .await
3712 .unwrap();
3713
3714 let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
3715 assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
3716 }
3717
3718 #[gpui::test]
3719 async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
3720 let fs = FakeFs::new(cx.background());
3721 fs.insert_tree(
3722 "/dir",
3723 json!({
3724 "file1": "the old contents",
3725 }),
3726 )
3727 .await;
3728
3729 let project = Project::test(fs.clone(), &mut cx);
3730 let worktree_id = project
3731 .update(&mut cx, |p, cx| {
3732 p.find_or_create_local_worktree("/dir/file1", false, cx)
3733 })
3734 .await
3735 .unwrap()
3736 .0
3737 .read_with(&cx, |tree, _| tree.id());
3738
3739 let buffer = project
3740 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, ""), cx))
3741 .await
3742 .unwrap();
3743 buffer
3744 .update(&mut cx, |buffer, cx| {
3745 buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
3746 buffer.save(cx)
3747 })
3748 .await
3749 .unwrap();
3750
3751 let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
3752 assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
3753 }
3754
3755 #[gpui::test(retries = 5)]
3756 async fn test_rescan_and_remote_updates(mut cx: gpui::TestAppContext) {
3757 let dir = temp_tree(json!({
3758 "a": {
3759 "file1": "",
3760 "file2": "",
3761 "file3": "",
3762 },
3763 "b": {
3764 "c": {
3765 "file4": "",
3766 "file5": "",
3767 }
3768 }
3769 }));
3770
3771 let project = Project::test(Arc::new(RealFs), &mut cx);
3772 let rpc = project.read_with(&cx, |p, _| p.client.clone());
3773
3774 let (tree, _) = project
3775 .update(&mut cx, |p, cx| {
3776 p.find_or_create_local_worktree(dir.path(), false, cx)
3777 })
3778 .await
3779 .unwrap();
3780 let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
3781
3782 let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| {
3783 let buffer = project.update(cx, |p, cx| p.open_buffer((worktree_id, path), cx));
3784 async move { buffer.await.unwrap() }
3785 };
3786 let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
3787 tree.read_with(cx, |tree, _| {
3788 tree.entry_for_path(path)
3789 .expect(&format!("no entry for path {}", path))
3790 .id
3791 })
3792 };
3793
3794 let buffer2 = buffer_for_path("a/file2", &mut cx).await;
3795 let buffer3 = buffer_for_path("a/file3", &mut cx).await;
3796 let buffer4 = buffer_for_path("b/c/file4", &mut cx).await;
3797 let buffer5 = buffer_for_path("b/c/file5", &mut cx).await;
3798
3799 let file2_id = id_for_path("a/file2", &cx);
3800 let file3_id = id_for_path("a/file3", &cx);
3801 let file4_id = id_for_path("b/c/file4", &cx);
3802
3803 // Wait for the initial scan.
3804 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
3805 .await;
3806
3807 // Create a remote copy of this worktree.
3808 let initial_snapshot = tree.read_with(&cx, |tree, _| tree.as_local().unwrap().snapshot());
3809 let (remote, load_task) = cx.update(|cx| {
3810 Worktree::remote(
3811 1,
3812 1,
3813 initial_snapshot.to_proto(&Default::default(), Default::default()),
3814 rpc.clone(),
3815 cx,
3816 )
3817 });
3818 load_task.await;
3819
3820 cx.read(|cx| {
3821 assert!(!buffer2.read(cx).is_dirty());
3822 assert!(!buffer3.read(cx).is_dirty());
3823 assert!(!buffer4.read(cx).is_dirty());
3824 assert!(!buffer5.read(cx).is_dirty());
3825 });
3826
3827 // Rename and delete files and directories.
3828 tree.flush_fs_events(&cx).await;
3829 std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
3830 std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
3831 std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
3832 std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
3833 tree.flush_fs_events(&cx).await;
3834
3835 let expected_paths = vec![
3836 "a",
3837 "a/file1",
3838 "a/file2.new",
3839 "b",
3840 "d",
3841 "d/file3",
3842 "d/file4",
3843 ];
3844
3845 cx.read(|app| {
3846 assert_eq!(
3847 tree.read(app)
3848 .paths()
3849 .map(|p| p.to_str().unwrap())
3850 .collect::<Vec<_>>(),
3851 expected_paths
3852 );
3853
3854 assert_eq!(id_for_path("a/file2.new", &cx), file2_id);
3855 assert_eq!(id_for_path("d/file3", &cx), file3_id);
3856 assert_eq!(id_for_path("d/file4", &cx), file4_id);
3857
3858 assert_eq!(
3859 buffer2.read(app).file().unwrap().path().as_ref(),
3860 Path::new("a/file2.new")
3861 );
3862 assert_eq!(
3863 buffer3.read(app).file().unwrap().path().as_ref(),
3864 Path::new("d/file3")
3865 );
3866 assert_eq!(
3867 buffer4.read(app).file().unwrap().path().as_ref(),
3868 Path::new("d/file4")
3869 );
3870 assert_eq!(
3871 buffer5.read(app).file().unwrap().path().as_ref(),
3872 Path::new("b/c/file5")
3873 );
3874
3875 assert!(!buffer2.read(app).file().unwrap().is_deleted());
3876 assert!(!buffer3.read(app).file().unwrap().is_deleted());
3877 assert!(!buffer4.read(app).file().unwrap().is_deleted());
3878 assert!(buffer5.read(app).file().unwrap().is_deleted());
3879 });
3880
3881 // Update the remote worktree. Check that it becomes consistent with the
3882 // local worktree.
3883 remote.update(&mut cx, |remote, cx| {
3884 let update_message = tree.read(cx).as_local().unwrap().snapshot().build_update(
3885 &initial_snapshot,
3886 1,
3887 1,
3888 true,
3889 );
3890 remote
3891 .as_remote_mut()
3892 .unwrap()
3893 .snapshot
3894 .apply_remote_update(update_message)
3895 .unwrap();
3896
3897 assert_eq!(
3898 remote
3899 .paths()
3900 .map(|p| p.to_str().unwrap())
3901 .collect::<Vec<_>>(),
3902 expected_paths
3903 );
3904 });
3905 }
3906
3907 #[gpui::test]
3908 async fn test_buffer_deduping(mut cx: gpui::TestAppContext) {
3909 let fs = FakeFs::new(cx.background());
3910 fs.insert_tree(
3911 "/the-dir",
3912 json!({
3913 "a.txt": "a-contents",
3914 "b.txt": "b-contents",
3915 }),
3916 )
3917 .await;
3918
3919 let project = Project::test(fs.clone(), &mut cx);
3920 let worktree_id = project
3921 .update(&mut cx, |p, cx| {
3922 p.find_or_create_local_worktree("/the-dir", false, cx)
3923 })
3924 .await
3925 .unwrap()
3926 .0
3927 .read_with(&cx, |tree, _| tree.id());
3928
3929 // Spawn multiple tasks to open paths, repeating some paths.
3930 let (buffer_a_1, buffer_b, buffer_a_2) = project.update(&mut cx, |p, cx| {
3931 (
3932 p.open_buffer((worktree_id, "a.txt"), cx),
3933 p.open_buffer((worktree_id, "b.txt"), cx),
3934 p.open_buffer((worktree_id, "a.txt"), cx),
3935 )
3936 });
3937
3938 let buffer_a_1 = buffer_a_1.await.unwrap();
3939 let buffer_a_2 = buffer_a_2.await.unwrap();
3940 let buffer_b = buffer_b.await.unwrap();
3941 assert_eq!(buffer_a_1.read_with(&cx, |b, _| b.text()), "a-contents");
3942 assert_eq!(buffer_b.read_with(&cx, |b, _| b.text()), "b-contents");
3943
3944 // There is only one buffer per path.
3945 let buffer_a_id = buffer_a_1.id();
3946 assert_eq!(buffer_a_2.id(), buffer_a_id);
3947
3948 // Open the same path again while it is still open.
3949 drop(buffer_a_1);
3950 let buffer_a_3 = project
3951 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
3952 .await
3953 .unwrap();
3954
3955 // There's still only one buffer per path.
3956 assert_eq!(buffer_a_3.id(), buffer_a_id);
3957 }
3958
3959 #[gpui::test]
3960 async fn test_buffer_is_dirty(mut cx: gpui::TestAppContext) {
3961 use std::fs;
3962
3963 let dir = temp_tree(json!({
3964 "file1": "abc",
3965 "file2": "def",
3966 "file3": "ghi",
3967 }));
3968
3969 let project = Project::test(Arc::new(RealFs), &mut cx);
3970 let (worktree, _) = project
3971 .update(&mut cx, |p, cx| {
3972 p.find_or_create_local_worktree(dir.path(), false, cx)
3973 })
3974 .await
3975 .unwrap();
3976 let worktree_id = worktree.read_with(&cx, |worktree, _| worktree.id());
3977
3978 worktree.flush_fs_events(&cx).await;
3979 worktree
3980 .read_with(&cx, |t, _| t.as_local().unwrap().scan_complete())
3981 .await;
3982
3983 let buffer1 = project
3984 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
3985 .await
3986 .unwrap();
3987 let events = Rc::new(RefCell::new(Vec::new()));
3988
3989 // initially, the buffer isn't dirty.
3990 buffer1.update(&mut cx, |buffer, cx| {
3991 cx.subscribe(&buffer1, {
3992 let events = events.clone();
3993 move |_, _, event, _| events.borrow_mut().push(event.clone())
3994 })
3995 .detach();
3996
3997 assert!(!buffer.is_dirty());
3998 assert!(events.borrow().is_empty());
3999
4000 buffer.edit(vec![1..2], "", cx);
4001 });
4002
4003 // after the first edit, the buffer is dirty, and emits a dirtied event.
4004 buffer1.update(&mut cx, |buffer, cx| {
4005 assert!(buffer.text() == "ac");
4006 assert!(buffer.is_dirty());
4007 assert_eq!(
4008 *events.borrow(),
4009 &[language::Event::Edited, language::Event::Dirtied]
4010 );
4011 events.borrow_mut().clear();
4012 buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), None, cx);
4013 });
4014
4015 // after saving, the buffer is not dirty, and emits a saved event.
4016 buffer1.update(&mut cx, |buffer, cx| {
4017 assert!(!buffer.is_dirty());
4018 assert_eq!(*events.borrow(), &[language::Event::Saved]);
4019 events.borrow_mut().clear();
4020
4021 buffer.edit(vec![1..1], "B", cx);
4022 buffer.edit(vec![2..2], "D", cx);
4023 });
4024
4025 // after editing again, the buffer is dirty, and emits another dirty event.
4026 buffer1.update(&mut cx, |buffer, cx| {
4027 assert!(buffer.text() == "aBDc");
4028 assert!(buffer.is_dirty());
4029 assert_eq!(
4030 *events.borrow(),
4031 &[
4032 language::Event::Edited,
4033 language::Event::Dirtied,
4034 language::Event::Edited,
4035 ],
4036 );
4037 events.borrow_mut().clear();
4038
4039 // TODO - currently, after restoring the buffer to its
4040 // previously-saved state, the is still considered dirty.
4041 buffer.edit([1..3], "", cx);
4042 assert!(buffer.text() == "ac");
4043 assert!(buffer.is_dirty());
4044 });
4045
4046 assert_eq!(*events.borrow(), &[language::Event::Edited]);
4047
4048 // When a file is deleted, the buffer is considered dirty.
4049 let events = Rc::new(RefCell::new(Vec::new()));
4050 let buffer2 = project
4051 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file2"), cx))
4052 .await
4053 .unwrap();
4054 buffer2.update(&mut cx, |_, cx| {
4055 cx.subscribe(&buffer2, {
4056 let events = events.clone();
4057 move |_, _, event, _| events.borrow_mut().push(event.clone())
4058 })
4059 .detach();
4060 });
4061
4062 fs::remove_file(dir.path().join("file2")).unwrap();
4063 buffer2.condition(&cx, |b, _| b.is_dirty()).await;
4064 assert_eq!(
4065 *events.borrow(),
4066 &[language::Event::Dirtied, language::Event::FileHandleChanged]
4067 );
4068
4069 // When a file is already dirty when deleted, we don't emit a Dirtied event.
4070 let events = Rc::new(RefCell::new(Vec::new()));
4071 let buffer3 = project
4072 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file3"), cx))
4073 .await
4074 .unwrap();
4075 buffer3.update(&mut cx, |_, cx| {
4076 cx.subscribe(&buffer3, {
4077 let events = events.clone();
4078 move |_, _, event, _| events.borrow_mut().push(event.clone())
4079 })
4080 .detach();
4081 });
4082
4083 worktree.flush_fs_events(&cx).await;
4084 buffer3.update(&mut cx, |buffer, cx| {
4085 buffer.edit(Some(0..0), "x", cx);
4086 });
4087 events.borrow_mut().clear();
4088 fs::remove_file(dir.path().join("file3")).unwrap();
4089 buffer3
4090 .condition(&cx, |_, _| !events.borrow().is_empty())
4091 .await;
4092 assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
4093 cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
4094 }
4095
4096 #[gpui::test]
4097 async fn test_buffer_file_changes_on_disk(mut cx: gpui::TestAppContext) {
4098 use std::fs;
4099
4100 let initial_contents = "aaa\nbbbbb\nc\n";
4101 let dir = temp_tree(json!({ "the-file": initial_contents }));
4102
4103 let project = Project::test(Arc::new(RealFs), &mut cx);
4104 let (worktree, _) = project
4105 .update(&mut cx, |p, cx| {
4106 p.find_or_create_local_worktree(dir.path(), false, cx)
4107 })
4108 .await
4109 .unwrap();
4110 let worktree_id = worktree.read_with(&cx, |tree, _| tree.id());
4111
4112 worktree
4113 .read_with(&cx, |t, _| t.as_local().unwrap().scan_complete())
4114 .await;
4115
4116 let abs_path = dir.path().join("the-file");
4117 let buffer = project
4118 .update(&mut cx, |p, cx| {
4119 p.open_buffer((worktree_id, "the-file"), cx)
4120 })
4121 .await
4122 .unwrap();
4123
4124 // TODO
4125 // Add a cursor on each row.
4126 // let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
4127 // assert!(!buffer.is_dirty());
4128 // buffer.add_selection_set(
4129 // &(0..3)
4130 // .map(|row| Selection {
4131 // id: row as usize,
4132 // start: Point::new(row, 1),
4133 // end: Point::new(row, 1),
4134 // reversed: false,
4135 // goal: SelectionGoal::None,
4136 // })
4137 // .collect::<Vec<_>>(),
4138 // cx,
4139 // )
4140 // });
4141
4142 // Change the file on disk, adding two new lines of text, and removing
4143 // one line.
4144 buffer.read_with(&cx, |buffer, _| {
4145 assert!(!buffer.is_dirty());
4146 assert!(!buffer.has_conflict());
4147 });
4148 let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
4149 fs::write(&abs_path, new_contents).unwrap();
4150
4151 // Because the buffer was not modified, it is reloaded from disk. Its
4152 // contents are edited according to the diff between the old and new
4153 // file contents.
4154 buffer
4155 .condition(&cx, |buffer, _| buffer.text() == new_contents)
4156 .await;
4157
4158 buffer.update(&mut cx, |buffer, _| {
4159 assert_eq!(buffer.text(), new_contents);
4160 assert!(!buffer.is_dirty());
4161 assert!(!buffer.has_conflict());
4162
4163 // TODO
4164 // let cursor_positions = buffer
4165 // .selection_set(selection_set_id)
4166 // .unwrap()
4167 // .selections::<Point>(&*buffer)
4168 // .map(|selection| {
4169 // assert_eq!(selection.start, selection.end);
4170 // selection.start
4171 // })
4172 // .collect::<Vec<_>>();
4173 // assert_eq!(
4174 // cursor_positions,
4175 // [Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
4176 // );
4177 });
4178
4179 // Modify the buffer
4180 buffer.update(&mut cx, |buffer, cx| {
4181 buffer.edit(vec![0..0], " ", cx);
4182 assert!(buffer.is_dirty());
4183 assert!(!buffer.has_conflict());
4184 });
4185
4186 // Change the file on disk again, adding blank lines to the beginning.
4187 fs::write(&abs_path, "\n\n\nAAAA\naaa\nBB\nbbbbb\n").unwrap();
4188
4189 // Because the buffer is modified, it doesn't reload from disk, but is
4190 // marked as having a conflict.
4191 buffer
4192 .condition(&cx, |buffer, _| buffer.has_conflict())
4193 .await;
4194 }
4195
4196 #[gpui::test]
4197 async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
4198 let fs = FakeFs::new(cx.background());
4199 fs.insert_tree(
4200 "/the-dir",
4201 json!({
4202 "a.rs": "
4203 fn foo(mut v: Vec<usize>) {
4204 for x in &v {
4205 v.push(1);
4206 }
4207 }
4208 "
4209 .unindent(),
4210 }),
4211 )
4212 .await;
4213
4214 let project = Project::test(fs.clone(), &mut cx);
4215 let (worktree, _) = project
4216 .update(&mut cx, |p, cx| {
4217 p.find_or_create_local_worktree("/the-dir", false, cx)
4218 })
4219 .await
4220 .unwrap();
4221 let worktree_id = worktree.read_with(&cx, |tree, _| tree.id());
4222
4223 let buffer = project
4224 .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
4225 .await
4226 .unwrap();
4227
4228 let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
4229 let message = lsp::PublishDiagnosticsParams {
4230 uri: buffer_uri.clone(),
4231 diagnostics: vec![
4232 lsp::Diagnostic {
4233 range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
4234 severity: Some(DiagnosticSeverity::WARNING),
4235 message: "error 1".to_string(),
4236 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
4237 location: lsp::Location {
4238 uri: buffer_uri.clone(),
4239 range: lsp::Range::new(
4240 lsp::Position::new(1, 8),
4241 lsp::Position::new(1, 9),
4242 ),
4243 },
4244 message: "error 1 hint 1".to_string(),
4245 }]),
4246 ..Default::default()
4247 },
4248 lsp::Diagnostic {
4249 range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
4250 severity: Some(DiagnosticSeverity::HINT),
4251 message: "error 1 hint 1".to_string(),
4252 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
4253 location: lsp::Location {
4254 uri: buffer_uri.clone(),
4255 range: lsp::Range::new(
4256 lsp::Position::new(1, 8),
4257 lsp::Position::new(1, 9),
4258 ),
4259 },
4260 message: "original diagnostic".to_string(),
4261 }]),
4262 ..Default::default()
4263 },
4264 lsp::Diagnostic {
4265 range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)),
4266 severity: Some(DiagnosticSeverity::ERROR),
4267 message: "error 2".to_string(),
4268 related_information: Some(vec![
4269 lsp::DiagnosticRelatedInformation {
4270 location: lsp::Location {
4271 uri: buffer_uri.clone(),
4272 range: lsp::Range::new(
4273 lsp::Position::new(1, 13),
4274 lsp::Position::new(1, 15),
4275 ),
4276 },
4277 message: "error 2 hint 1".to_string(),
4278 },
4279 lsp::DiagnosticRelatedInformation {
4280 location: lsp::Location {
4281 uri: buffer_uri.clone(),
4282 range: lsp::Range::new(
4283 lsp::Position::new(1, 13),
4284 lsp::Position::new(1, 15),
4285 ),
4286 },
4287 message: "error 2 hint 2".to_string(),
4288 },
4289 ]),
4290 ..Default::default()
4291 },
4292 lsp::Diagnostic {
4293 range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
4294 severity: Some(DiagnosticSeverity::HINT),
4295 message: "error 2 hint 1".to_string(),
4296 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
4297 location: lsp::Location {
4298 uri: buffer_uri.clone(),
4299 range: lsp::Range::new(
4300 lsp::Position::new(2, 8),
4301 lsp::Position::new(2, 17),
4302 ),
4303 },
4304 message: "original diagnostic".to_string(),
4305 }]),
4306 ..Default::default()
4307 },
4308 lsp::Diagnostic {
4309 range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
4310 severity: Some(DiagnosticSeverity::HINT),
4311 message: "error 2 hint 2".to_string(),
4312 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
4313 location: lsp::Location {
4314 uri: buffer_uri.clone(),
4315 range: lsp::Range::new(
4316 lsp::Position::new(2, 8),
4317 lsp::Position::new(2, 17),
4318 ),
4319 },
4320 message: "original diagnostic".to_string(),
4321 }]),
4322 ..Default::default()
4323 },
4324 ],
4325 version: None,
4326 };
4327
4328 project
4329 .update(&mut cx, |p, cx| {
4330 p.update_diagnostics(message, &Default::default(), cx)
4331 })
4332 .unwrap();
4333 let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
4334
4335 assert_eq!(
4336 buffer
4337 .diagnostics_in_range::<_, Point>(0..buffer.len())
4338 .collect::<Vec<_>>(),
4339 &[
4340 DiagnosticEntry {
4341 range: Point::new(1, 8)..Point::new(1, 9),
4342 diagnostic: Diagnostic {
4343 severity: DiagnosticSeverity::WARNING,
4344 message: "error 1".to_string(),
4345 group_id: 0,
4346 is_primary: true,
4347 ..Default::default()
4348 }
4349 },
4350 DiagnosticEntry {
4351 range: Point::new(1, 8)..Point::new(1, 9),
4352 diagnostic: Diagnostic {
4353 severity: DiagnosticSeverity::HINT,
4354 message: "error 1 hint 1".to_string(),
4355 group_id: 0,
4356 is_primary: false,
4357 ..Default::default()
4358 }
4359 },
4360 DiagnosticEntry {
4361 range: Point::new(1, 13)..Point::new(1, 15),
4362 diagnostic: Diagnostic {
4363 severity: DiagnosticSeverity::HINT,
4364 message: "error 2 hint 1".to_string(),
4365 group_id: 1,
4366 is_primary: false,
4367 ..Default::default()
4368 }
4369 },
4370 DiagnosticEntry {
4371 range: Point::new(1, 13)..Point::new(1, 15),
4372 diagnostic: Diagnostic {
4373 severity: DiagnosticSeverity::HINT,
4374 message: "error 2 hint 2".to_string(),
4375 group_id: 1,
4376 is_primary: false,
4377 ..Default::default()
4378 }
4379 },
4380 DiagnosticEntry {
4381 range: Point::new(2, 8)..Point::new(2, 17),
4382 diagnostic: Diagnostic {
4383 severity: DiagnosticSeverity::ERROR,
4384 message: "error 2".to_string(),
4385 group_id: 1,
4386 is_primary: true,
4387 ..Default::default()
4388 }
4389 }
4390 ]
4391 );
4392
4393 assert_eq!(
4394 buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
4395 &[
4396 DiagnosticEntry {
4397 range: Point::new(1, 8)..Point::new(1, 9),
4398 diagnostic: Diagnostic {
4399 severity: DiagnosticSeverity::WARNING,
4400 message: "error 1".to_string(),
4401 group_id: 0,
4402 is_primary: true,
4403 ..Default::default()
4404 }
4405 },
4406 DiagnosticEntry {
4407 range: Point::new(1, 8)..Point::new(1, 9),
4408 diagnostic: Diagnostic {
4409 severity: DiagnosticSeverity::HINT,
4410 message: "error 1 hint 1".to_string(),
4411 group_id: 0,
4412 is_primary: false,
4413 ..Default::default()
4414 }
4415 },
4416 ]
4417 );
4418 assert_eq!(
4419 buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
4420 &[
4421 DiagnosticEntry {
4422 range: Point::new(1, 13)..Point::new(1, 15),
4423 diagnostic: Diagnostic {
4424 severity: DiagnosticSeverity::HINT,
4425 message: "error 2 hint 1".to_string(),
4426 group_id: 1,
4427 is_primary: false,
4428 ..Default::default()
4429 }
4430 },
4431 DiagnosticEntry {
4432 range: Point::new(1, 13)..Point::new(1, 15),
4433 diagnostic: Diagnostic {
4434 severity: DiagnosticSeverity::HINT,
4435 message: "error 2 hint 2".to_string(),
4436 group_id: 1,
4437 is_primary: false,
4438 ..Default::default()
4439 }
4440 },
4441 DiagnosticEntry {
4442 range: Point::new(2, 8)..Point::new(2, 17),
4443 diagnostic: Diagnostic {
4444 severity: DiagnosticSeverity::ERROR,
4445 message: "error 2".to_string(),
4446 group_id: 1,
4447 is_primary: true,
4448 ..Default::default()
4449 }
4450 }
4451 ]
4452 );
4453 }
4454
4455 #[gpui::test]
4456 async fn test_rename(mut cx: gpui::TestAppContext) {
4457 let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
4458 let language = Arc::new(Language::new(
4459 LanguageConfig {
4460 name: "Rust".into(),
4461 path_suffixes: vec!["rs".to_string()],
4462 language_server: Some(language_server_config),
4463 ..Default::default()
4464 },
4465 Some(tree_sitter_rust::language()),
4466 ));
4467
4468 let fs = FakeFs::new(cx.background());
4469 fs.insert_tree(
4470 "/dir",
4471 json!({
4472 "one.rs": "const ONE: usize = 1;",
4473 "two.rs": "const TWO: usize = one::ONE + one::ONE;"
4474 }),
4475 )
4476 .await;
4477
4478 let project = Project::test(fs.clone(), &mut cx);
4479 project.update(&mut cx, |project, _| {
4480 Arc::get_mut(&mut project.languages).unwrap().add(language);
4481 });
4482
4483 let (tree, _) = project
4484 .update(&mut cx, |project, cx| {
4485 project.find_or_create_local_worktree("/dir", false, cx)
4486 })
4487 .await
4488 .unwrap();
4489 let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
4490 cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
4491 .await;
4492
4493 let buffer = project
4494 .update(&mut cx, |project, cx| {
4495 project.open_buffer((worktree_id, Path::new("one.rs")), cx)
4496 })
4497 .await
4498 .unwrap();
4499
4500 let mut fake_server = fake_servers.next().await.unwrap();
4501
4502 let response = project.update(&mut cx, |project, cx| {
4503 project.prepare_rename(buffer.clone(), 7, cx)
4504 });
4505 fake_server
4506 .handle_request::<lsp::request::PrepareRenameRequest, _>(|params, _| {
4507 assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
4508 assert_eq!(params.position, lsp::Position::new(0, 7));
4509 Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
4510 lsp::Position::new(0, 6),
4511 lsp::Position::new(0, 9),
4512 )))
4513 })
4514 .next()
4515 .await
4516 .unwrap();
4517 let range = response.await.unwrap().unwrap();
4518 let range = buffer.read_with(&cx, |buffer, _| range.to_offset(buffer));
4519 assert_eq!(range, 6..9);
4520
4521 let response = project.update(&mut cx, |project, cx| {
4522 project.perform_rename(buffer.clone(), 7, "THREE".to_string(), true, cx)
4523 });
4524 fake_server
4525 .handle_request::<lsp::request::Rename, _>(|params, _| {
4526 assert_eq!(
4527 params.text_document_position.text_document.uri.as_str(),
4528 "file:///dir/one.rs"
4529 );
4530 assert_eq!(
4531 params.text_document_position.position,
4532 lsp::Position::new(0, 7)
4533 );
4534 assert_eq!(params.new_name, "THREE");
4535 Some(lsp::WorkspaceEdit {
4536 changes: Some(
4537 [
4538 (
4539 lsp::Url::from_file_path("/dir/one.rs").unwrap(),
4540 vec![lsp::TextEdit::new(
4541 lsp::Range::new(
4542 lsp::Position::new(0, 6),
4543 lsp::Position::new(0, 9),
4544 ),
4545 "THREE".to_string(),
4546 )],
4547 ),
4548 (
4549 lsp::Url::from_file_path("/dir/two.rs").unwrap(),
4550 vec![
4551 lsp::TextEdit::new(
4552 lsp::Range::new(
4553 lsp::Position::new(0, 24),
4554 lsp::Position::new(0, 27),
4555 ),
4556 "THREE".to_string(),
4557 ),
4558 lsp::TextEdit::new(
4559 lsp::Range::new(
4560 lsp::Position::new(0, 35),
4561 lsp::Position::new(0, 38),
4562 ),
4563 "THREE".to_string(),
4564 ),
4565 ],
4566 ),
4567 ]
4568 .into_iter()
4569 .collect(),
4570 ),
4571 ..Default::default()
4572 })
4573 })
4574 .next()
4575 .await
4576 .unwrap();
4577 let mut transaction = response.await.unwrap().0;
4578 assert_eq!(transaction.len(), 2);
4579 assert_eq!(
4580 transaction
4581 .remove_entry(&buffer)
4582 .unwrap()
4583 .0
4584 .read_with(&cx, |buffer, _| buffer.text()),
4585 "const THREE: usize = 1;"
4586 );
4587 assert_eq!(
4588 transaction
4589 .into_keys()
4590 .next()
4591 .unwrap()
4592 .read_with(&cx, |buffer, _| buffer.text()),
4593 "const TWO: usize = one::THREE + one::THREE;"
4594 );
4595 }
4596}