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