1mod db;
2pub mod fs;
3mod ignore;
4mod lsp_command;
5pub mod search;
6pub mod worktree;
7
8#[cfg(test)]
9mod project_tests;
10
11use anyhow::{anyhow, Context, Result};
12use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
13use clock::ReplicaId;
14use collections::{hash_map, BTreeMap, HashMap, HashSet};
15use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, SinkExt, StreamExt, TryFutureExt};
16use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
17use gpui::{
18 AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
19 MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle,
20};
21use language::{
22 point_to_lsp,
23 proto::{
24 deserialize_anchor, deserialize_line_ending, deserialize_version, serialize_anchor,
25 serialize_version,
26 },
27 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CharKind, CodeAction, CodeLabel,
28 Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _,
29 Language, LanguageRegistry, LanguageServerName, LineEnding, LocalFile, LspAdapter,
30 OffsetRangeExt, Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16,
31 Transaction,
32};
33use lsp::{
34 DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString,
35 MarkedString,
36};
37use lsp_command::*;
38use parking_lot::Mutex;
39use postage::stream::Stream;
40use postage::watch;
41use rand::prelude::*;
42use search::SearchQuery;
43use serde::Serialize;
44use settings::Settings;
45use sha2::{Digest, Sha256};
46use similar::{ChangeTag, TextDiff};
47use std::{
48 cell::RefCell,
49 cmp::{self, Ordering},
50 convert::TryInto,
51 ffi::OsString,
52 hash::Hash,
53 mem,
54 num::NonZeroU32,
55 ops::Range,
56 os::unix::{ffi::OsStrExt, prelude::OsStringExt},
57 path::{Component, Path, PathBuf},
58 rc::Rc,
59 str,
60 sync::{
61 atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
62 Arc,
63 },
64 time::Instant,
65};
66use thiserror::Error;
67use util::{post_inc, ResultExt, TryFutureExt as _};
68
69pub use db::Db;
70pub use fs::*;
71pub use worktree::*;
72
73pub trait Item: Entity {
74 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
75}
76
77pub struct ProjectStore {
78 db: Arc<Db>,
79 projects: Vec<WeakModelHandle<Project>>,
80}
81
82// Language server state is stored across 3 collections:
83// language_servers =>
84// a mapping from unique server id to LanguageServerState which can either be a task for a
85// server in the process of starting, or a running server with adapter and language server arcs
86// language_server_ids => a mapping from worktreeId and server name to the unique server id
87// language_server_statuses => a mapping from unique server id to the current server status
88//
89// Multiple worktrees can map to the same language server for example when you jump to the definition
90// of a file in the standard library. So language_server_ids is used to look up which server is active
91// for a given worktree and language server name
92//
93// When starting a language server, first the id map is checked to make sure a server isn't already available
94// for that worktree. If there is one, it finishes early. Otherwise, a new id is allocated and and
95// the Starting variant of LanguageServerState is stored in the language_servers map.
96pub struct Project {
97 worktrees: Vec<WorktreeHandle>,
98 active_entry: Option<ProjectEntryId>,
99 languages: Arc<LanguageRegistry>,
100 language_servers: HashMap<usize, LanguageServerState>,
101 language_server_ids: HashMap<(WorktreeId, LanguageServerName), usize>,
102 language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
103 language_server_settings: Arc<Mutex<serde_json::Value>>,
104 last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
105 next_language_server_id: usize,
106 client: Arc<client::Client>,
107 next_entry_id: Arc<AtomicUsize>,
108 next_diagnostic_group_id: usize,
109 user_store: ModelHandle<UserStore>,
110 project_store: ModelHandle<ProjectStore>,
111 fs: Arc<dyn Fs>,
112 client_state: ProjectClientState,
113 collaborators: HashMap<PeerId, Collaborator>,
114 client_subscriptions: Vec<client::Subscription>,
115 _subscriptions: Vec<gpui::Subscription>,
116 opened_buffer: (Rc<RefCell<watch::Sender<()>>>, watch::Receiver<()>),
117 shared_buffers: HashMap<PeerId, HashSet<u64>>,
118 loading_buffers: HashMap<
119 ProjectPath,
120 postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
121 >,
122 loading_local_worktrees:
123 HashMap<Arc<Path>, Shared<Task<Result<ModelHandle<Worktree>, Arc<anyhow::Error>>>>>,
124 opened_buffers: HashMap<u64, OpenBuffer>,
125 buffer_snapshots: HashMap<u64, Vec<(i32, TextBufferSnapshot)>>,
126 nonce: u128,
127 initialized_persistent_state: bool,
128}
129
130#[derive(Error, Debug)]
131pub enum JoinProjectError {
132 #[error("host declined join request")]
133 HostDeclined,
134 #[error("host closed the project")]
135 HostClosedProject,
136 #[error("host went offline")]
137 HostWentOffline,
138 #[error("{0}")]
139 Other(#[from] anyhow::Error),
140}
141
142enum OpenBuffer {
143 Strong(ModelHandle<Buffer>),
144 Weak(WeakModelHandle<Buffer>),
145 Loading(Vec<Operation>),
146}
147
148enum WorktreeHandle {
149 Strong(ModelHandle<Worktree>),
150 Weak(WeakModelHandle<Worktree>),
151}
152
153enum ProjectClientState {
154 Local {
155 is_shared: bool,
156 remote_id_tx: watch::Sender<Option<u64>>,
157 remote_id_rx: watch::Receiver<Option<u64>>,
158 online_tx: watch::Sender<bool>,
159 online_rx: watch::Receiver<bool>,
160 _maintain_remote_id: Task<Option<()>>,
161 _maintain_online_status: Task<Option<()>>,
162 },
163 Remote {
164 sharing_has_stopped: bool,
165 remote_id: u64,
166 replica_id: ReplicaId,
167 _detect_unshare: Task<Option<()>>,
168 },
169}
170
171#[derive(Clone, Debug)]
172pub struct Collaborator {
173 pub user: Arc<User>,
174 pub peer_id: PeerId,
175 pub replica_id: ReplicaId,
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
179pub enum Event {
180 ActiveEntryChanged(Option<ProjectEntryId>),
181 WorktreeAdded,
182 WorktreeRemoved(WorktreeId),
183 DiskBasedDiagnosticsStarted {
184 language_server_id: usize,
185 },
186 DiskBasedDiagnosticsFinished {
187 language_server_id: usize,
188 },
189 DiagnosticsUpdated {
190 path: ProjectPath,
191 language_server_id: usize,
192 },
193 RemoteIdChanged(Option<u64>),
194 CollaboratorLeft(PeerId),
195 ContactRequestedJoin(Arc<User>),
196 ContactCancelledJoinRequest(Arc<User>),
197}
198
199pub enum LanguageServerState {
200 Starting(Task<Option<Arc<LanguageServer>>>),
201 Running {
202 adapter: Arc<dyn LspAdapter>,
203 server: Arc<LanguageServer>,
204 },
205}
206
207#[derive(Serialize)]
208pub struct LanguageServerStatus {
209 pub name: String,
210 pub pending_work: BTreeMap<String, LanguageServerProgress>,
211 pub has_pending_diagnostic_updates: bool,
212 progress_tokens: HashSet<String>,
213}
214
215#[derive(Clone, Debug, Serialize)]
216pub struct LanguageServerProgress {
217 pub message: Option<String>,
218 pub percentage: Option<usize>,
219 #[serde(skip_serializing)]
220 pub last_update_at: Instant,
221}
222
223#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
224pub struct ProjectPath {
225 pub worktree_id: WorktreeId,
226 pub path: Arc<Path>,
227}
228
229#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
230pub struct DiagnosticSummary {
231 pub language_server_id: usize,
232 pub error_count: usize,
233 pub warning_count: usize,
234}
235
236#[derive(Debug, Clone)]
237pub struct Location {
238 pub buffer: ModelHandle<Buffer>,
239 pub range: Range<language::Anchor>,
240}
241
242#[derive(Debug, Clone)]
243pub struct LocationLink {
244 pub origin: Option<Location>,
245 pub target: Location,
246}
247
248#[derive(Debug)]
249pub struct DocumentHighlight {
250 pub range: Range<language::Anchor>,
251 pub kind: DocumentHighlightKind,
252}
253
254#[derive(Clone, Debug)]
255pub struct Symbol {
256 pub source_worktree_id: WorktreeId,
257 pub worktree_id: WorktreeId,
258 pub language_server_name: LanguageServerName,
259 pub path: PathBuf,
260 pub label: CodeLabel,
261 pub name: String,
262 pub kind: lsp::SymbolKind,
263 pub range: Range<PointUtf16>,
264 pub signature: [u8; 32],
265}
266
267#[derive(Clone, Debug, PartialEq)]
268pub struct HoverBlock {
269 pub text: String,
270 pub language: Option<String>,
271}
272
273impl HoverBlock {
274 fn try_new(marked_string: MarkedString) -> Option<Self> {
275 let result = match marked_string {
276 MarkedString::LanguageString(LanguageString { language, value }) => HoverBlock {
277 text: value,
278 language: Some(language),
279 },
280 MarkedString::String(text) => HoverBlock {
281 text,
282 language: None,
283 },
284 };
285 if result.text.is_empty() {
286 None
287 } else {
288 Some(result)
289 }
290 }
291}
292
293#[derive(Debug)]
294pub struct Hover {
295 pub contents: Vec<HoverBlock>,
296 pub range: Option<Range<language::Anchor>>,
297}
298
299#[derive(Default)]
300pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
301
302impl DiagnosticSummary {
303 fn new<'a, T: 'a>(
304 language_server_id: usize,
305 diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>,
306 ) -> Self {
307 let mut this = Self {
308 language_server_id,
309 error_count: 0,
310 warning_count: 0,
311 };
312
313 for entry in diagnostics {
314 if entry.diagnostic.is_primary {
315 match entry.diagnostic.severity {
316 DiagnosticSeverity::ERROR => this.error_count += 1,
317 DiagnosticSeverity::WARNING => this.warning_count += 1,
318 _ => {}
319 }
320 }
321 }
322
323 this
324 }
325
326 pub fn is_empty(&self) -> bool {
327 self.error_count == 0 && self.warning_count == 0
328 }
329
330 pub fn to_proto(&self, path: &Path) -> proto::DiagnosticSummary {
331 proto::DiagnosticSummary {
332 path: path.to_string_lossy().to_string(),
333 language_server_id: self.language_server_id as u64,
334 error_count: self.error_count as u32,
335 warning_count: self.warning_count as u32,
336 }
337 }
338}
339
340#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
341pub struct ProjectEntryId(usize);
342
343impl ProjectEntryId {
344 pub const MAX: Self = Self(usize::MAX);
345
346 pub fn new(counter: &AtomicUsize) -> Self {
347 Self(counter.fetch_add(1, SeqCst))
348 }
349
350 pub fn from_proto(id: u64) -> Self {
351 Self(id as usize)
352 }
353
354 pub fn to_proto(&self) -> u64 {
355 self.0 as u64
356 }
357
358 pub fn to_usize(&self) -> usize {
359 self.0
360 }
361}
362
363impl Project {
364 pub fn init(client: &Arc<Client>) {
365 client.add_model_message_handler(Self::handle_request_join_project);
366 client.add_model_message_handler(Self::handle_add_collaborator);
367 client.add_model_message_handler(Self::handle_buffer_reloaded);
368 client.add_model_message_handler(Self::handle_buffer_saved);
369 client.add_model_message_handler(Self::handle_start_language_server);
370 client.add_model_message_handler(Self::handle_update_language_server);
371 client.add_model_message_handler(Self::handle_remove_collaborator);
372 client.add_model_message_handler(Self::handle_join_project_request_cancelled);
373 client.add_model_message_handler(Self::handle_update_project);
374 client.add_model_message_handler(Self::handle_unregister_project);
375 client.add_model_message_handler(Self::handle_project_unshared);
376 client.add_model_message_handler(Self::handle_update_buffer_file);
377 client.add_model_message_handler(Self::handle_update_buffer);
378 client.add_model_message_handler(Self::handle_update_diagnostic_summary);
379 client.add_model_message_handler(Self::handle_update_worktree);
380 client.add_model_request_handler(Self::handle_create_project_entry);
381 client.add_model_request_handler(Self::handle_rename_project_entry);
382 client.add_model_request_handler(Self::handle_copy_project_entry);
383 client.add_model_request_handler(Self::handle_delete_project_entry);
384 client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
385 client.add_model_request_handler(Self::handle_apply_code_action);
386 client.add_model_request_handler(Self::handle_reload_buffers);
387 client.add_model_request_handler(Self::handle_format_buffers);
388 client.add_model_request_handler(Self::handle_get_code_actions);
389 client.add_model_request_handler(Self::handle_get_completions);
390 client.add_model_request_handler(Self::handle_lsp_command::<GetHover>);
391 client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
392 client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
393 client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
394 client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
395 client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
396 client.add_model_request_handler(Self::handle_search_project);
397 client.add_model_request_handler(Self::handle_get_project_symbols);
398 client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
399 client.add_model_request_handler(Self::handle_open_buffer_by_id);
400 client.add_model_request_handler(Self::handle_open_buffer_by_path);
401 client.add_model_request_handler(Self::handle_save_buffer);
402 }
403
404 pub fn local(
405 online: bool,
406 client: Arc<Client>,
407 user_store: ModelHandle<UserStore>,
408 project_store: ModelHandle<ProjectStore>,
409 languages: Arc<LanguageRegistry>,
410 fs: Arc<dyn Fs>,
411 cx: &mut MutableAppContext,
412 ) -> ModelHandle<Self> {
413 cx.add_model(|cx: &mut ModelContext<Self>| {
414 let (remote_id_tx, remote_id_rx) = watch::channel();
415 let _maintain_remote_id = cx.spawn_weak({
416 let mut status_rx = client.clone().status();
417 move |this, mut cx| async move {
418 while let Some(status) = status_rx.recv().await {
419 let this = this.upgrade(&cx)?;
420 if status.is_connected() {
421 this.update(&mut cx, |this, cx| this.register(cx))
422 .await
423 .log_err()?;
424 } else {
425 this.update(&mut cx, |this, cx| this.unregister(cx))
426 .await
427 .log_err();
428 }
429 }
430 None
431 }
432 });
433
434 let (online_tx, online_rx) = watch::channel_with(online);
435 let _maintain_online_status = cx.spawn_weak({
436 let mut online_rx = online_rx.clone();
437 move |this, mut cx| async move {
438 while let Some(online) = online_rx.recv().await {
439 let this = this.upgrade(&cx)?;
440 this.update(&mut cx, |this, cx| {
441 if !online {
442 this.unshared(cx);
443 }
444 this.metadata_changed(false, cx)
445 });
446 }
447 None
448 }
449 });
450
451 let handle = cx.weak_handle();
452 project_store.update(cx, |store, cx| store.add_project(handle, cx));
453
454 let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
455 Self {
456 worktrees: Default::default(),
457 collaborators: Default::default(),
458 opened_buffers: Default::default(),
459 shared_buffers: Default::default(),
460 loading_buffers: Default::default(),
461 loading_local_worktrees: Default::default(),
462 buffer_snapshots: Default::default(),
463 client_state: ProjectClientState::Local {
464 is_shared: false,
465 remote_id_tx,
466 remote_id_rx,
467 online_tx,
468 online_rx,
469 _maintain_remote_id,
470 _maintain_online_status,
471 },
472 opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
473 client_subscriptions: Vec::new(),
474 _subscriptions: vec![cx.observe_global::<Settings, _>(Self::on_settings_changed)],
475 active_entry: None,
476 languages,
477 client,
478 user_store,
479 project_store,
480 fs,
481 next_entry_id: Default::default(),
482 next_diagnostic_group_id: Default::default(),
483 language_servers: Default::default(),
484 language_server_ids: Default::default(),
485 language_server_statuses: Default::default(),
486 last_workspace_edits_by_language_server: Default::default(),
487 language_server_settings: Default::default(),
488 next_language_server_id: 0,
489 nonce: StdRng::from_entropy().gen(),
490 initialized_persistent_state: false,
491 }
492 })
493 }
494
495 pub async fn remote(
496 remote_id: u64,
497 client: Arc<Client>,
498 user_store: ModelHandle<UserStore>,
499 project_store: ModelHandle<ProjectStore>,
500 languages: Arc<LanguageRegistry>,
501 fs: Arc<dyn Fs>,
502 mut cx: AsyncAppContext,
503 ) -> Result<ModelHandle<Self>, JoinProjectError> {
504 client.authenticate_and_connect(true, &cx).await?;
505
506 let response = client
507 .request(proto::JoinProject {
508 project_id: remote_id,
509 })
510 .await?;
511
512 let response = match response.variant.ok_or_else(|| anyhow!("missing variant"))? {
513 proto::join_project_response::Variant::Accept(response) => response,
514 proto::join_project_response::Variant::Decline(decline) => {
515 match proto::join_project_response::decline::Reason::from_i32(decline.reason) {
516 Some(proto::join_project_response::decline::Reason::Declined) => {
517 Err(JoinProjectError::HostDeclined)?
518 }
519 Some(proto::join_project_response::decline::Reason::Closed) => {
520 Err(JoinProjectError::HostClosedProject)?
521 }
522 Some(proto::join_project_response::decline::Reason::WentOffline) => {
523 Err(JoinProjectError::HostWentOffline)?
524 }
525 None => Err(anyhow!("missing decline reason"))?,
526 }
527 }
528 };
529
530 let replica_id = response.replica_id as ReplicaId;
531
532 let mut worktrees = Vec::new();
533 for worktree in response.worktrees {
534 let worktree = cx
535 .update(|cx| Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx));
536 worktrees.push(worktree);
537 }
538
539 let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
540 let this = cx.add_model(|cx: &mut ModelContext<Self>| {
541 let handle = cx.weak_handle();
542 project_store.update(cx, |store, cx| store.add_project(handle, cx));
543
544 let mut this = Self {
545 worktrees: Vec::new(),
546 loading_buffers: Default::default(),
547 opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
548 shared_buffers: Default::default(),
549 loading_local_worktrees: Default::default(),
550 active_entry: None,
551 collaborators: Default::default(),
552 languages,
553 user_store: user_store.clone(),
554 project_store,
555 fs,
556 next_entry_id: Default::default(),
557 next_diagnostic_group_id: Default::default(),
558 client_subscriptions: vec![client.add_model_for_remote_entity(remote_id, cx)],
559 _subscriptions: Default::default(),
560 client: client.clone(),
561 client_state: ProjectClientState::Remote {
562 sharing_has_stopped: false,
563 remote_id,
564 replica_id,
565 _detect_unshare: cx.spawn_weak(move |this, mut cx| {
566 async move {
567 let mut status = client.status();
568 let is_connected =
569 status.next().await.map_or(false, |s| s.is_connected());
570 // Even if we're initially connected, any future change of the status means we momentarily disconnected.
571 if !is_connected || status.next().await.is_some() {
572 if let Some(this) = this.upgrade(&cx) {
573 this.update(&mut cx, |this, cx| this.removed_from_project(cx))
574 }
575 }
576 Ok(())
577 }
578 .log_err()
579 }),
580 },
581 language_servers: Default::default(),
582 language_server_ids: Default::default(),
583 language_server_settings: Default::default(),
584 language_server_statuses: response
585 .language_servers
586 .into_iter()
587 .map(|server| {
588 (
589 server.id as usize,
590 LanguageServerStatus {
591 name: server.name,
592 pending_work: Default::default(),
593 has_pending_diagnostic_updates: false,
594 progress_tokens: Default::default(),
595 },
596 )
597 })
598 .collect(),
599 last_workspace_edits_by_language_server: Default::default(),
600 next_language_server_id: 0,
601 opened_buffers: Default::default(),
602 buffer_snapshots: Default::default(),
603 nonce: StdRng::from_entropy().gen(),
604 initialized_persistent_state: false,
605 };
606 for worktree in worktrees {
607 this.add_worktree(&worktree, cx);
608 }
609 this
610 });
611
612 let user_ids = response
613 .collaborators
614 .iter()
615 .map(|peer| peer.user_id)
616 .collect();
617 user_store
618 .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))
619 .await?;
620 let mut collaborators = HashMap::default();
621 for message in response.collaborators {
622 let collaborator = Collaborator::from_proto(message, &user_store, &mut cx).await?;
623 collaborators.insert(collaborator.peer_id, collaborator);
624 }
625
626 this.update(&mut cx, |this, _| {
627 this.collaborators = collaborators;
628 });
629
630 Ok(this)
631 }
632
633 #[cfg(any(test, feature = "test-support"))]
634 pub async fn test(
635 fs: Arc<dyn Fs>,
636 root_paths: impl IntoIterator<Item = &Path>,
637 cx: &mut gpui::TestAppContext,
638 ) -> ModelHandle<Project> {
639 if !cx.read(|cx| cx.has_global::<Settings>()) {
640 cx.update(|cx| cx.set_global(Settings::test(cx)));
641 }
642
643 let languages = Arc::new(LanguageRegistry::test());
644 let http_client = client::test::FakeHttpClient::with_404_response();
645 let client = client::Client::new(http_client.clone());
646 let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
647 let project_store = cx.add_model(|_| ProjectStore::new(Db::open_fake()));
648 let project = cx.update(|cx| {
649 Project::local(true, client, user_store, project_store, languages, fs, cx)
650 });
651 for path in root_paths {
652 let (tree, _) = project
653 .update(cx, |project, cx| {
654 project.find_or_create_local_worktree(path, true, cx)
655 })
656 .await
657 .unwrap();
658 tree.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
659 .await;
660 }
661 project
662 }
663
664 pub fn restore_state(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
665 if self.is_remote() {
666 return Task::ready(Ok(()));
667 }
668
669 let db = self.project_store.read(cx).db.clone();
670 let keys = self.db_keys_for_online_state(cx);
671 let online_by_default = cx.global::<Settings>().projects_online_by_default;
672 let read_online = cx.background().spawn(async move {
673 let values = db.read(keys)?;
674 anyhow::Ok(
675 values
676 .into_iter()
677 .all(|e| e.map_or(online_by_default, |e| e == [true as u8])),
678 )
679 });
680 cx.spawn(|this, mut cx| async move {
681 let online = read_online.await.log_err().unwrap_or(false);
682 this.update(&mut cx, |this, cx| {
683 this.initialized_persistent_state = true;
684 if let ProjectClientState::Local { online_tx, .. } = &mut this.client_state {
685 let mut online_tx = online_tx.borrow_mut();
686 if *online_tx != online {
687 *online_tx = online;
688 drop(online_tx);
689 this.metadata_changed(false, cx);
690 }
691 }
692 });
693 Ok(())
694 })
695 }
696
697 fn persist_state(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
698 if self.is_remote() || !self.initialized_persistent_state {
699 return Task::ready(Ok(()));
700 }
701
702 let db = self.project_store.read(cx).db.clone();
703 let keys = self.db_keys_for_online_state(cx);
704 let is_online = self.is_online();
705 cx.background().spawn(async move {
706 let value = &[is_online as u8];
707 db.write(keys.into_iter().map(|key| (key, value)))
708 })
709 }
710
711 fn on_settings_changed(&mut self, cx: &mut ModelContext<'_, Self>) {
712 let settings = cx.global::<Settings>();
713
714 let mut language_servers_to_start = Vec::new();
715 for buffer in self.opened_buffers.values() {
716 if let Some(buffer) = buffer.upgrade(cx) {
717 let buffer = buffer.read(cx);
718 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language())
719 {
720 if settings.enable_language_server(Some(&language.name())) {
721 let worktree = file.worktree.read(cx);
722 language_servers_to_start.push((
723 worktree.id(),
724 worktree.as_local().unwrap().abs_path().clone(),
725 language.clone(),
726 ));
727 }
728 }
729 }
730 }
731
732 let mut language_servers_to_stop = Vec::new();
733 for language in self.languages.to_vec() {
734 if let Some(lsp_adapter) = language.lsp_adapter() {
735 if !settings.enable_language_server(Some(&language.name())) {
736 let lsp_name = &lsp_adapter.name;
737 for (worktree_id, started_lsp_name) in self.language_servers.keys() {
738 if lsp_name == started_lsp_name {
739 language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
740 }
741 }
742 }
743 }
744 }
745
746 // Stop all newly-disabled language servers.
747 for (worktree_id, adapter_name) in language_servers_to_stop {
748 self.stop_language_server(worktree_id, adapter_name, cx)
749 .detach();
750 }
751
752 // Start all the newly-enabled language servers.
753 for (worktree_id, worktree_path, language) in language_servers_to_start {
754 self.start_language_server(worktree_id, worktree_path, language, cx);
755 }
756
757 cx.notify();
758 }
759
760 pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
761 self.opened_buffers
762 .get(&remote_id)
763 .and_then(|buffer| buffer.upgrade(cx))
764 }
765
766 pub fn languages(&self) -> &Arc<LanguageRegistry> {
767 &self.languages
768 }
769
770 pub fn client(&self) -> Arc<Client> {
771 self.client.clone()
772 }
773
774 pub fn user_store(&self) -> ModelHandle<UserStore> {
775 self.user_store.clone()
776 }
777
778 pub fn project_store(&self) -> ModelHandle<ProjectStore> {
779 self.project_store.clone()
780 }
781
782 #[cfg(any(test, feature = "test-support"))]
783 pub fn check_invariants(&self, cx: &AppContext) {
784 if self.is_local() {
785 let mut worktree_root_paths = HashMap::default();
786 for worktree in self.worktrees(cx) {
787 let worktree = worktree.read(cx);
788 let abs_path = worktree.as_local().unwrap().abs_path().clone();
789 let prev_worktree_id = worktree_root_paths.insert(abs_path.clone(), worktree.id());
790 assert_eq!(
791 prev_worktree_id,
792 None,
793 "abs path {:?} for worktree {:?} is not unique ({:?} was already registered with the same path)",
794 abs_path,
795 worktree.id(),
796 prev_worktree_id
797 )
798 }
799 } else {
800 let replica_id = self.replica_id();
801 for buffer in self.opened_buffers.values() {
802 if let Some(buffer) = buffer.upgrade(cx) {
803 let buffer = buffer.read(cx);
804 assert_eq!(
805 buffer.deferred_ops_len(),
806 0,
807 "replica {}, buffer {} has deferred operations",
808 replica_id,
809 buffer.remote_id()
810 );
811 }
812 }
813 }
814 }
815
816 #[cfg(any(test, feature = "test-support"))]
817 pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
818 let path = path.into();
819 if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
820 self.opened_buffers.iter().any(|(_, buffer)| {
821 if let Some(buffer) = buffer.upgrade(cx) {
822 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
823 if file.worktree == worktree && file.path() == &path.path {
824 return true;
825 }
826 }
827 }
828 false
829 })
830 } else {
831 false
832 }
833 }
834
835 pub fn fs(&self) -> &Arc<dyn Fs> {
836 &self.fs
837 }
838
839 pub fn set_online(&mut self, online: bool, _: &mut ModelContext<Self>) {
840 if let ProjectClientState::Local { online_tx, .. } = &mut self.client_state {
841 let mut online_tx = online_tx.borrow_mut();
842 if *online_tx != online {
843 *online_tx = online;
844 }
845 }
846 }
847
848 pub fn is_online(&self) -> bool {
849 match &self.client_state {
850 ProjectClientState::Local { online_rx, .. } => *online_rx.borrow(),
851 ProjectClientState::Remote { .. } => true,
852 }
853 }
854
855 fn unregister(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
856 self.unshared(cx);
857 if let ProjectClientState::Local { remote_id_rx, .. } = &mut self.client_state {
858 if let Some(remote_id) = *remote_id_rx.borrow() {
859 let request = self.client.request(proto::UnregisterProject {
860 project_id: remote_id,
861 });
862 return cx.spawn(|this, mut cx| async move {
863 let response = request.await;
864
865 // Unregistering the project causes the server to send out a
866 // contact update removing this project from the host's list
867 // of online projects. Wait until this contact update has been
868 // processed before clearing out this project's remote id, so
869 // that there is no moment where this project appears in the
870 // contact metadata and *also* has no remote id.
871 this.update(&mut cx, |this, cx| {
872 this.user_store()
873 .update(cx, |store, _| store.contact_updates_done())
874 })
875 .await;
876
877 this.update(&mut cx, |this, cx| {
878 if let ProjectClientState::Local { remote_id_tx, .. } =
879 &mut this.client_state
880 {
881 *remote_id_tx.borrow_mut() = None;
882 }
883 this.client_subscriptions.clear();
884 this.metadata_changed(false, cx);
885 });
886 response.map(drop)
887 });
888 }
889 }
890 Task::ready(Ok(()))
891 }
892
893 fn register(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
894 if let ProjectClientState::Local {
895 remote_id_rx,
896 online_rx,
897 ..
898 } = &self.client_state
899 {
900 if remote_id_rx.borrow().is_some() {
901 return Task::ready(Ok(()));
902 }
903
904 let response = self.client.request(proto::RegisterProject {
905 online: *online_rx.borrow(),
906 });
907 cx.spawn(|this, mut cx| async move {
908 let remote_id = response.await?.project_id;
909 this.update(&mut cx, |this, cx| {
910 if let ProjectClientState::Local { remote_id_tx, .. } = &mut this.client_state {
911 *remote_id_tx.borrow_mut() = Some(remote_id);
912 }
913
914 this.metadata_changed(false, cx);
915 cx.emit(Event::RemoteIdChanged(Some(remote_id)));
916 this.client_subscriptions
917 .push(this.client.add_model_for_remote_entity(remote_id, cx));
918 Ok(())
919 })
920 })
921 } else {
922 Task::ready(Err(anyhow!("can't register a remote project")))
923 }
924 }
925
926 pub fn remote_id(&self) -> Option<u64> {
927 match &self.client_state {
928 ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
929 ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
930 }
931 }
932
933 pub fn next_remote_id(&self) -> impl Future<Output = u64> {
934 let mut id = None;
935 let mut watch = None;
936 match &self.client_state {
937 ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
938 ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
939 }
940
941 async move {
942 if let Some(id) = id {
943 return id;
944 }
945 let mut watch = watch.unwrap();
946 loop {
947 let id = *watch.borrow();
948 if let Some(id) = id {
949 return id;
950 }
951 watch.next().await;
952 }
953 }
954 }
955
956 pub fn shared_remote_id(&self) -> Option<u64> {
957 match &self.client_state {
958 ProjectClientState::Local {
959 remote_id_rx,
960 is_shared,
961 ..
962 } => {
963 if *is_shared {
964 *remote_id_rx.borrow()
965 } else {
966 None
967 }
968 }
969 ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
970 }
971 }
972
973 pub fn replica_id(&self) -> ReplicaId {
974 match &self.client_state {
975 ProjectClientState::Local { .. } => 0,
976 ProjectClientState::Remote { replica_id, .. } => *replica_id,
977 }
978 }
979
980 fn metadata_changed(&mut self, persist: bool, cx: &mut ModelContext<Self>) {
981 if let ProjectClientState::Local {
982 remote_id_rx,
983 online_rx,
984 ..
985 } = &self.client_state
986 {
987 // Broadcast worktrees only if the project is online.
988 let worktrees = if *online_rx.borrow() {
989 self.worktrees
990 .iter()
991 .filter_map(|worktree| {
992 worktree
993 .upgrade(&cx)
994 .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto())
995 })
996 .collect()
997 } else {
998 Default::default()
999 };
1000 if let Some(project_id) = *remote_id_rx.borrow() {
1001 let online = *online_rx.borrow();
1002 self.client
1003 .send(proto::UpdateProject {
1004 project_id,
1005 worktrees,
1006 online,
1007 })
1008 .log_err();
1009
1010 if online {
1011 let worktrees = self.visible_worktrees(cx).collect::<Vec<_>>();
1012 let scans_complete =
1013 futures::future::join_all(worktrees.iter().filter_map(|worktree| {
1014 Some(worktree.read(cx).as_local()?.scan_complete())
1015 }));
1016
1017 let worktrees = worktrees.into_iter().map(|handle| handle.downgrade());
1018 cx.spawn_weak(move |_, cx| async move {
1019 scans_complete.await;
1020 cx.read(|cx| {
1021 for worktree in worktrees {
1022 if let Some(worktree) = worktree
1023 .upgrade(cx)
1024 .and_then(|worktree| worktree.read(cx).as_local())
1025 {
1026 worktree.send_extension_counts(project_id);
1027 }
1028 }
1029 })
1030 })
1031 .detach();
1032 }
1033 }
1034
1035 self.project_store.update(cx, |_, cx| cx.notify());
1036 if persist {
1037 self.persist_state(cx).detach_and_log_err(cx);
1038 }
1039 cx.notify();
1040 }
1041 }
1042
1043 pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
1044 &self.collaborators
1045 }
1046
1047 pub fn worktrees<'a>(
1048 &'a self,
1049 cx: &'a AppContext,
1050 ) -> impl 'a + DoubleEndedIterator<Item = ModelHandle<Worktree>> {
1051 self.worktrees
1052 .iter()
1053 .filter_map(move |worktree| worktree.upgrade(cx))
1054 }
1055
1056 pub fn visible_worktrees<'a>(
1057 &'a self,
1058 cx: &'a AppContext,
1059 ) -> impl 'a + DoubleEndedIterator<Item = ModelHandle<Worktree>> {
1060 self.worktrees.iter().filter_map(|worktree| {
1061 worktree.upgrade(cx).and_then(|worktree| {
1062 if worktree.read(cx).is_visible() {
1063 Some(worktree)
1064 } else {
1065 None
1066 }
1067 })
1068 })
1069 }
1070
1071 pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
1072 self.visible_worktrees(cx)
1073 .map(|tree| tree.read(cx).root_name())
1074 }
1075
1076 fn db_keys_for_online_state(&self, cx: &AppContext) -> Vec<String> {
1077 self.worktrees
1078 .iter()
1079 .filter_map(|worktree| {
1080 let worktree = worktree.upgrade(&cx)?.read(cx);
1081 if worktree.is_visible() {
1082 Some(format!(
1083 "project-path-online:{}",
1084 worktree.as_local().unwrap().abs_path().to_string_lossy()
1085 ))
1086 } else {
1087 None
1088 }
1089 })
1090 .collect::<Vec<_>>()
1091 }
1092
1093 pub fn worktree_for_id(
1094 &self,
1095 id: WorktreeId,
1096 cx: &AppContext,
1097 ) -> Option<ModelHandle<Worktree>> {
1098 self.worktrees(cx)
1099 .find(|worktree| worktree.read(cx).id() == id)
1100 }
1101
1102 pub fn worktree_for_entry(
1103 &self,
1104 entry_id: ProjectEntryId,
1105 cx: &AppContext,
1106 ) -> Option<ModelHandle<Worktree>> {
1107 self.worktrees(cx)
1108 .find(|worktree| worktree.read(cx).contains_entry(entry_id))
1109 }
1110
1111 pub fn worktree_id_for_entry(
1112 &self,
1113 entry_id: ProjectEntryId,
1114 cx: &AppContext,
1115 ) -> Option<WorktreeId> {
1116 self.worktree_for_entry(entry_id, cx)
1117 .map(|worktree| worktree.read(cx).id())
1118 }
1119
1120 pub fn contains_paths(&self, paths: &[PathBuf], cx: &AppContext) -> bool {
1121 paths.iter().all(|path| self.contains_path(&path, cx))
1122 }
1123
1124 pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool {
1125 for worktree in self.worktrees(cx) {
1126 let worktree = worktree.read(cx).as_local();
1127 if worktree.map_or(false, |w| w.contains_abs_path(path)) {
1128 return true;
1129 }
1130 }
1131 false
1132 }
1133
1134 pub fn create_entry(
1135 &mut self,
1136 project_path: impl Into<ProjectPath>,
1137 is_directory: bool,
1138 cx: &mut ModelContext<Self>,
1139 ) -> Option<Task<Result<Entry>>> {
1140 let project_path = project_path.into();
1141 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
1142 if self.is_local() {
1143 Some(worktree.update(cx, |worktree, cx| {
1144 worktree
1145 .as_local_mut()
1146 .unwrap()
1147 .create_entry(project_path.path, is_directory, cx)
1148 }))
1149 } else {
1150 let client = self.client.clone();
1151 let project_id = self.remote_id().unwrap();
1152 Some(cx.spawn_weak(|_, mut cx| async move {
1153 let response = client
1154 .request(proto::CreateProjectEntry {
1155 worktree_id: project_path.worktree_id.to_proto(),
1156 project_id,
1157 path: project_path.path.as_os_str().as_bytes().to_vec(),
1158 is_directory,
1159 })
1160 .await?;
1161 let entry = response
1162 .entry
1163 .ok_or_else(|| anyhow!("missing entry in response"))?;
1164 worktree
1165 .update(&mut cx, |worktree, cx| {
1166 worktree.as_remote_mut().unwrap().insert_entry(
1167 entry,
1168 response.worktree_scan_id as usize,
1169 cx,
1170 )
1171 })
1172 .await
1173 }))
1174 }
1175 }
1176
1177 pub fn copy_entry(
1178 &mut self,
1179 entry_id: ProjectEntryId,
1180 new_path: impl Into<Arc<Path>>,
1181 cx: &mut ModelContext<Self>,
1182 ) -> Option<Task<Result<Entry>>> {
1183 let worktree = self.worktree_for_entry(entry_id, cx)?;
1184 let new_path = new_path.into();
1185 if self.is_local() {
1186 worktree.update(cx, |worktree, cx| {
1187 worktree
1188 .as_local_mut()
1189 .unwrap()
1190 .copy_entry(entry_id, new_path, cx)
1191 })
1192 } else {
1193 let client = self.client.clone();
1194 let project_id = self.remote_id().unwrap();
1195
1196 Some(cx.spawn_weak(|_, mut cx| async move {
1197 let response = client
1198 .request(proto::CopyProjectEntry {
1199 project_id,
1200 entry_id: entry_id.to_proto(),
1201 new_path: new_path.as_os_str().as_bytes().to_vec(),
1202 })
1203 .await?;
1204 let entry = response
1205 .entry
1206 .ok_or_else(|| anyhow!("missing entry in response"))?;
1207 worktree
1208 .update(&mut cx, |worktree, cx| {
1209 worktree.as_remote_mut().unwrap().insert_entry(
1210 entry,
1211 response.worktree_scan_id as usize,
1212 cx,
1213 )
1214 })
1215 .await
1216 }))
1217 }
1218 }
1219
1220 pub fn rename_entry(
1221 &mut self,
1222 entry_id: ProjectEntryId,
1223 new_path: impl Into<Arc<Path>>,
1224 cx: &mut ModelContext<Self>,
1225 ) -> Option<Task<Result<Entry>>> {
1226 let worktree = self.worktree_for_entry(entry_id, cx)?;
1227 let new_path = new_path.into();
1228 if self.is_local() {
1229 worktree.update(cx, |worktree, cx| {
1230 worktree
1231 .as_local_mut()
1232 .unwrap()
1233 .rename_entry(entry_id, new_path, cx)
1234 })
1235 } else {
1236 let client = self.client.clone();
1237 let project_id = self.remote_id().unwrap();
1238
1239 Some(cx.spawn_weak(|_, mut cx| async move {
1240 let response = client
1241 .request(proto::RenameProjectEntry {
1242 project_id,
1243 entry_id: entry_id.to_proto(),
1244 new_path: new_path.as_os_str().as_bytes().to_vec(),
1245 })
1246 .await?;
1247 let entry = response
1248 .entry
1249 .ok_or_else(|| anyhow!("missing entry in response"))?;
1250 worktree
1251 .update(&mut cx, |worktree, cx| {
1252 worktree.as_remote_mut().unwrap().insert_entry(
1253 entry,
1254 response.worktree_scan_id as usize,
1255 cx,
1256 )
1257 })
1258 .await
1259 }))
1260 }
1261 }
1262
1263 pub fn delete_entry(
1264 &mut self,
1265 entry_id: ProjectEntryId,
1266 cx: &mut ModelContext<Self>,
1267 ) -> Option<Task<Result<()>>> {
1268 let worktree = self.worktree_for_entry(entry_id, cx)?;
1269 if self.is_local() {
1270 worktree.update(cx, |worktree, cx| {
1271 worktree.as_local_mut().unwrap().delete_entry(entry_id, cx)
1272 })
1273 } else {
1274 let client = self.client.clone();
1275 let project_id = self.remote_id().unwrap();
1276 Some(cx.spawn_weak(|_, mut cx| async move {
1277 let response = client
1278 .request(proto::DeleteProjectEntry {
1279 project_id,
1280 entry_id: entry_id.to_proto(),
1281 })
1282 .await?;
1283 worktree
1284 .update(&mut cx, move |worktree, cx| {
1285 worktree.as_remote_mut().unwrap().delete_entry(
1286 entry_id,
1287 response.worktree_scan_id as usize,
1288 cx,
1289 )
1290 })
1291 .await
1292 }))
1293 }
1294 }
1295
1296 fn share(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
1297 if !self.is_online() {
1298 return Task::ready(Err(anyhow!("can't share an offline project")));
1299 }
1300
1301 let project_id;
1302 if let ProjectClientState::Local {
1303 remote_id_rx,
1304 is_shared,
1305 ..
1306 } = &mut self.client_state
1307 {
1308 if *is_shared {
1309 return Task::ready(Ok(()));
1310 }
1311 *is_shared = true;
1312 if let Some(id) = *remote_id_rx.borrow() {
1313 project_id = id;
1314 } else {
1315 return Task::ready(Err(anyhow!("project hasn't been registered")));
1316 }
1317 } else {
1318 return Task::ready(Err(anyhow!("can't share a remote project")));
1319 };
1320
1321 for open_buffer in self.opened_buffers.values_mut() {
1322 match open_buffer {
1323 OpenBuffer::Strong(_) => {}
1324 OpenBuffer::Weak(buffer) => {
1325 if let Some(buffer) = buffer.upgrade(cx) {
1326 *open_buffer = OpenBuffer::Strong(buffer);
1327 }
1328 }
1329 OpenBuffer::Loading(_) => unreachable!(),
1330 }
1331 }
1332
1333 for worktree_handle in self.worktrees.iter_mut() {
1334 match worktree_handle {
1335 WorktreeHandle::Strong(_) => {}
1336 WorktreeHandle::Weak(worktree) => {
1337 if let Some(worktree) = worktree.upgrade(cx) {
1338 *worktree_handle = WorktreeHandle::Strong(worktree);
1339 }
1340 }
1341 }
1342 }
1343
1344 let mut tasks = Vec::new();
1345 for worktree in self.worktrees(cx).collect::<Vec<_>>() {
1346 worktree.update(cx, |worktree, cx| {
1347 let worktree = worktree.as_local_mut().unwrap();
1348 tasks.push(worktree.share(project_id, cx));
1349 });
1350 }
1351
1352 for (server_id, status) in &self.language_server_statuses {
1353 self.client
1354 .send(proto::StartLanguageServer {
1355 project_id,
1356 server: Some(proto::LanguageServer {
1357 id: *server_id as u64,
1358 name: status.name.clone(),
1359 }),
1360 })
1361 .log_err();
1362 }
1363
1364 cx.spawn(|this, mut cx| async move {
1365 for task in tasks {
1366 task.await?;
1367 }
1368 this.update(&mut cx, |_, cx| cx.notify());
1369 Ok(())
1370 })
1371 }
1372
1373 fn unshared(&mut self, cx: &mut ModelContext<Self>) {
1374 if let ProjectClientState::Local { is_shared, .. } = &mut self.client_state {
1375 if !*is_shared {
1376 return;
1377 }
1378
1379 *is_shared = false;
1380 self.collaborators.clear();
1381 self.shared_buffers.clear();
1382 for worktree_handle in self.worktrees.iter_mut() {
1383 if let WorktreeHandle::Strong(worktree) = worktree_handle {
1384 let is_visible = worktree.update(cx, |worktree, _| {
1385 worktree.as_local_mut().unwrap().unshare();
1386 worktree.is_visible()
1387 });
1388 if !is_visible {
1389 *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
1390 }
1391 }
1392 }
1393
1394 for open_buffer in self.opened_buffers.values_mut() {
1395 match open_buffer {
1396 OpenBuffer::Strong(buffer) => {
1397 *open_buffer = OpenBuffer::Weak(buffer.downgrade());
1398 }
1399 _ => {}
1400 }
1401 }
1402
1403 cx.notify();
1404 } else {
1405 log::error!("attempted to unshare a remote project");
1406 }
1407 }
1408
1409 pub fn respond_to_join_request(
1410 &mut self,
1411 requester_id: u64,
1412 allow: bool,
1413 cx: &mut ModelContext<Self>,
1414 ) {
1415 if let Some(project_id) = self.remote_id() {
1416 let share = if self.is_online() && allow {
1417 Some(self.share(cx))
1418 } else {
1419 None
1420 };
1421 let client = self.client.clone();
1422 cx.foreground()
1423 .spawn(async move {
1424 client.send(proto::RespondToJoinProjectRequest {
1425 requester_id,
1426 project_id,
1427 allow,
1428 })?;
1429 if let Some(share) = share {
1430 share.await?;
1431 }
1432 anyhow::Ok(())
1433 })
1434 .detach_and_log_err(cx);
1435 }
1436 }
1437
1438 fn removed_from_project(&mut self, cx: &mut ModelContext<Self>) {
1439 if let ProjectClientState::Remote {
1440 sharing_has_stopped,
1441 ..
1442 } = &mut self.client_state
1443 {
1444 *sharing_has_stopped = true;
1445 self.collaborators.clear();
1446 for worktree in &self.worktrees {
1447 if let Some(worktree) = worktree.upgrade(cx) {
1448 worktree.update(cx, |worktree, _| {
1449 if let Some(worktree) = worktree.as_remote_mut() {
1450 worktree.disconnected_from_host();
1451 }
1452 });
1453 }
1454 }
1455 cx.notify();
1456 }
1457 }
1458
1459 pub fn is_read_only(&self) -> bool {
1460 match &self.client_state {
1461 ProjectClientState::Local { .. } => false,
1462 ProjectClientState::Remote {
1463 sharing_has_stopped,
1464 ..
1465 } => *sharing_has_stopped,
1466 }
1467 }
1468
1469 pub fn is_local(&self) -> bool {
1470 match &self.client_state {
1471 ProjectClientState::Local { .. } => true,
1472 ProjectClientState::Remote { .. } => false,
1473 }
1474 }
1475
1476 pub fn is_remote(&self) -> bool {
1477 !self.is_local()
1478 }
1479
1480 pub fn create_buffer(
1481 &mut self,
1482 text: &str,
1483 language: Option<Arc<Language>>,
1484 cx: &mut ModelContext<Self>,
1485 ) -> Result<ModelHandle<Buffer>> {
1486 if self.is_remote() {
1487 return Err(anyhow!("creating buffers as a guest is not supported yet"));
1488 }
1489
1490 let buffer = cx.add_model(|cx| {
1491 Buffer::new(self.replica_id(), text, cx)
1492 .with_language(language.unwrap_or(language::PLAIN_TEXT.clone()), cx)
1493 });
1494 self.register_buffer(&buffer, cx)?;
1495 Ok(buffer)
1496 }
1497
1498 pub fn open_path(
1499 &mut self,
1500 path: impl Into<ProjectPath>,
1501 cx: &mut ModelContext<Self>,
1502 ) -> Task<Result<(ProjectEntryId, AnyModelHandle)>> {
1503 let task = self.open_buffer(path, cx);
1504 cx.spawn_weak(|_, cx| async move {
1505 let buffer = task.await?;
1506 let project_entry_id = buffer
1507 .read_with(&cx, |buffer, cx| {
1508 File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1509 })
1510 .ok_or_else(|| anyhow!("no project entry"))?;
1511 Ok((project_entry_id, buffer.into()))
1512 })
1513 }
1514
1515 pub fn open_local_buffer(
1516 &mut self,
1517 abs_path: impl AsRef<Path>,
1518 cx: &mut ModelContext<Self>,
1519 ) -> Task<Result<ModelHandle<Buffer>>> {
1520 if let Some((worktree, relative_path)) = self.find_local_worktree(abs_path.as_ref(), cx) {
1521 self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1522 } else {
1523 Task::ready(Err(anyhow!("no such path")))
1524 }
1525 }
1526
1527 pub fn open_buffer(
1528 &mut self,
1529 path: impl Into<ProjectPath>,
1530 cx: &mut ModelContext<Self>,
1531 ) -> Task<Result<ModelHandle<Buffer>>> {
1532 let project_path = path.into();
1533 let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
1534 worktree
1535 } else {
1536 return Task::ready(Err(anyhow!("no such worktree")));
1537 };
1538
1539 // If there is already a buffer for the given path, then return it.
1540 let existing_buffer = self.get_open_buffer(&project_path, cx);
1541 if let Some(existing_buffer) = existing_buffer {
1542 return Task::ready(Ok(existing_buffer));
1543 }
1544
1545 let mut loading_watch = match self.loading_buffers.entry(project_path.clone()) {
1546 // If the given path is already being loaded, then wait for that existing
1547 // task to complete and return the same buffer.
1548 hash_map::Entry::Occupied(e) => e.get().clone(),
1549
1550 // Otherwise, record the fact that this path is now being loaded.
1551 hash_map::Entry::Vacant(entry) => {
1552 let (mut tx, rx) = postage::watch::channel();
1553 entry.insert(rx.clone());
1554
1555 let load_buffer = if worktree.read(cx).is_local() {
1556 self.open_local_buffer_internal(&project_path.path, &worktree, cx)
1557 } else {
1558 self.open_remote_buffer_internal(&project_path.path, &worktree, cx)
1559 };
1560
1561 cx.spawn(move |this, mut cx| async move {
1562 let load_result = load_buffer.await;
1563 *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
1564 // Record the fact that the buffer is no longer loading.
1565 this.loading_buffers.remove(&project_path);
1566 let buffer = load_result.map_err(Arc::new)?;
1567 Ok(buffer)
1568 }));
1569 })
1570 .detach();
1571 rx
1572 }
1573 };
1574
1575 cx.foreground().spawn(async move {
1576 loop {
1577 if let Some(result) = loading_watch.borrow().as_ref() {
1578 match result {
1579 Ok(buffer) => return Ok(buffer.clone()),
1580 Err(error) => return Err(anyhow!("{}", error)),
1581 }
1582 }
1583 loading_watch.next().await;
1584 }
1585 })
1586 }
1587
1588 fn open_local_buffer_internal(
1589 &mut self,
1590 path: &Arc<Path>,
1591 worktree: &ModelHandle<Worktree>,
1592 cx: &mut ModelContext<Self>,
1593 ) -> Task<Result<ModelHandle<Buffer>>> {
1594 let load_buffer = worktree.update(cx, |worktree, cx| {
1595 let worktree = worktree.as_local_mut().unwrap();
1596 worktree.load_buffer(path, cx)
1597 });
1598 cx.spawn(|this, mut cx| async move {
1599 let buffer = load_buffer.await?;
1600 this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
1601 Ok(buffer)
1602 })
1603 }
1604
1605 fn open_remote_buffer_internal(
1606 &mut self,
1607 path: &Arc<Path>,
1608 worktree: &ModelHandle<Worktree>,
1609 cx: &mut ModelContext<Self>,
1610 ) -> Task<Result<ModelHandle<Buffer>>> {
1611 let rpc = self.client.clone();
1612 let project_id = self.remote_id().unwrap();
1613 let remote_worktree_id = worktree.read(cx).id();
1614 let path = path.clone();
1615 let path_string = path.to_string_lossy().to_string();
1616 cx.spawn(|this, mut cx| async move {
1617 let response = rpc
1618 .request(proto::OpenBufferByPath {
1619 project_id,
1620 worktree_id: remote_worktree_id.to_proto(),
1621 path: path_string,
1622 })
1623 .await?;
1624 let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
1625 this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
1626 .await
1627 })
1628 }
1629
1630 fn open_local_buffer_via_lsp(
1631 &mut self,
1632 abs_path: lsp::Url,
1633 language_server_id: usize,
1634 language_server_name: LanguageServerName,
1635 cx: &mut ModelContext<Self>,
1636 ) -> Task<Result<ModelHandle<Buffer>>> {
1637 cx.spawn(|this, mut cx| async move {
1638 let abs_path = abs_path
1639 .to_file_path()
1640 .map_err(|_| anyhow!("can't convert URI to path"))?;
1641 let (worktree, relative_path) = if let Some(result) =
1642 this.read_with(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
1643 {
1644 result
1645 } else {
1646 let worktree = this
1647 .update(&mut cx, |this, cx| {
1648 this.create_local_worktree(&abs_path, false, cx)
1649 })
1650 .await?;
1651 let name = lsp_adapter.name.clone();
1652 this.update(&mut cx, |this, cx| {
1653 this.language_servers
1654 .insert((worktree.read(cx).id(), name), (lsp_adapter, lsp_server));
1655 });
1656 (worktree, PathBuf::new())
1657 };
1658
1659 let project_path = ProjectPath {
1660 worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
1661 path: relative_path.into(),
1662 };
1663 this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
1664 .await
1665 })
1666 }
1667
1668 pub fn open_buffer_by_id(
1669 &mut self,
1670 id: u64,
1671 cx: &mut ModelContext<Self>,
1672 ) -> Task<Result<ModelHandle<Buffer>>> {
1673 if let Some(buffer) = self.buffer_for_id(id, cx) {
1674 Task::ready(Ok(buffer))
1675 } else if self.is_local() {
1676 Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1677 } else if let Some(project_id) = self.remote_id() {
1678 let request = self
1679 .client
1680 .request(proto::OpenBufferById { project_id, id });
1681 cx.spawn(|this, mut cx| async move {
1682 let buffer = request
1683 .await?
1684 .buffer
1685 .ok_or_else(|| anyhow!("invalid buffer"))?;
1686 this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
1687 .await
1688 })
1689 } else {
1690 Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1691 }
1692 }
1693
1694 pub fn save_buffer_as(
1695 &mut self,
1696 buffer: ModelHandle<Buffer>,
1697 abs_path: PathBuf,
1698 cx: &mut ModelContext<Project>,
1699 ) -> Task<Result<()>> {
1700 let worktree_task = self.find_or_create_local_worktree(&abs_path, true, cx);
1701 let old_path =
1702 File::from_dyn(buffer.read(cx).file()).and_then(|f| Some(f.as_local()?.abs_path(cx)));
1703 cx.spawn(|this, mut cx| async move {
1704 if let Some(old_path) = old_path {
1705 this.update(&mut cx, |this, cx| {
1706 this.unregister_buffer_from_language_server(&buffer, old_path, cx);
1707 });
1708 }
1709 let (worktree, path) = worktree_task.await?;
1710 worktree
1711 .update(&mut cx, |worktree, cx| {
1712 worktree
1713 .as_local_mut()
1714 .unwrap()
1715 .save_buffer_as(buffer.clone(), path, cx)
1716 })
1717 .await?;
1718 this.update(&mut cx, |this, cx| {
1719 this.assign_language_to_buffer(&buffer, cx);
1720 this.register_buffer_with_language_server(&buffer, cx)
1721 });
1722 Ok(())
1723 })
1724 }
1725
1726 pub fn get_open_buffer(
1727 &mut self,
1728 path: &ProjectPath,
1729 cx: &mut ModelContext<Self>,
1730 ) -> Option<ModelHandle<Buffer>> {
1731 let worktree = self.worktree_for_id(path.worktree_id, cx)?;
1732 self.opened_buffers.values().find_map(|buffer| {
1733 let buffer = buffer.upgrade(cx)?;
1734 let file = File::from_dyn(buffer.read(cx).file())?;
1735 if file.worktree == worktree && file.path() == &path.path {
1736 Some(buffer)
1737 } else {
1738 None
1739 }
1740 })
1741 }
1742
1743 fn register_buffer(
1744 &mut self,
1745 buffer: &ModelHandle<Buffer>,
1746 cx: &mut ModelContext<Self>,
1747 ) -> Result<()> {
1748 let remote_id = buffer.read(cx).remote_id();
1749 let open_buffer = if self.is_remote() || self.is_shared() {
1750 OpenBuffer::Strong(buffer.clone())
1751 } else {
1752 OpenBuffer::Weak(buffer.downgrade())
1753 };
1754
1755 match self.opened_buffers.insert(remote_id, open_buffer) {
1756 None => {}
1757 Some(OpenBuffer::Loading(operations)) => {
1758 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?
1759 }
1760 Some(OpenBuffer::Weak(existing_handle)) => {
1761 if existing_handle.upgrade(cx).is_some() {
1762 Err(anyhow!(
1763 "already registered buffer with remote id {}",
1764 remote_id
1765 ))?
1766 }
1767 }
1768 Some(OpenBuffer::Strong(_)) => Err(anyhow!(
1769 "already registered buffer with remote id {}",
1770 remote_id
1771 ))?,
1772 }
1773 cx.subscribe(buffer, |this, buffer, event, cx| {
1774 this.on_buffer_event(buffer, event, cx);
1775 })
1776 .detach();
1777
1778 self.assign_language_to_buffer(buffer, cx);
1779 self.register_buffer_with_language_server(buffer, cx);
1780 cx.observe_release(buffer, |this, buffer, cx| {
1781 if let Some(file) = File::from_dyn(buffer.file()) {
1782 if file.is_local() {
1783 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1784 if let Some((_, server)) = this.language_server_for_buffer(buffer, cx) {
1785 server
1786 .notify::<lsp::notification::DidCloseTextDocument>(
1787 lsp::DidCloseTextDocumentParams {
1788 text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
1789 },
1790 )
1791 .log_err();
1792 }
1793 }
1794 }
1795 })
1796 .detach();
1797
1798 Ok(())
1799 }
1800
1801 fn register_buffer_with_language_server(
1802 &mut self,
1803 buffer_handle: &ModelHandle<Buffer>,
1804 cx: &mut ModelContext<'_, Self>,
1805 ) {
1806 let buffer = buffer_handle.read(cx);
1807 let buffer_id = buffer.remote_id();
1808 if let Some(file) = File::from_dyn(buffer.file()) {
1809 if file.is_local() {
1810 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1811 let initial_snapshot = buffer.text_snapshot();
1812
1813 let mut language_server = None;
1814 let mut language_id = None;
1815 if let Some(language) = buffer.language() {
1816 let worktree_id = file.worktree_id(cx);
1817 if let Some(adapter) = language.lsp_adapter() {
1818 language_id = adapter.id_for_language.clone();
1819 language_server = self
1820 .language_server_ids
1821 .get(&(worktree_id, adapter.name.clone()))
1822 .and_then(|id| self.language_servers.get(&id))
1823 .and_then(|server_state| {
1824 if let LanguageServerState::Running { server, .. } = server_state {
1825 Some(server.clone())
1826 } else {
1827 None
1828 }
1829 });
1830 }
1831 }
1832
1833 if let Some(local_worktree) = file.worktree.read(cx).as_local() {
1834 if let Some(diagnostics) = local_worktree.diagnostics_for_path(file.path()) {
1835 self.update_buffer_diagnostics(&buffer_handle, diagnostics, None, cx)
1836 .log_err();
1837 }
1838 }
1839
1840 if let Some(server) = language_server {
1841 server
1842 .notify::<lsp::notification::DidOpenTextDocument>(
1843 lsp::DidOpenTextDocumentParams {
1844 text_document: lsp::TextDocumentItem::new(
1845 uri,
1846 language_id.unwrap_or_default(),
1847 0,
1848 initial_snapshot.text(),
1849 ),
1850 }
1851 .clone(),
1852 )
1853 .log_err();
1854 buffer_handle.update(cx, |buffer, cx| {
1855 buffer.set_completion_triggers(
1856 server
1857 .capabilities()
1858 .completion_provider
1859 .as_ref()
1860 .and_then(|provider| provider.trigger_characters.clone())
1861 .unwrap_or(Vec::new()),
1862 cx,
1863 )
1864 });
1865 self.buffer_snapshots
1866 .insert(buffer_id, vec![(0, initial_snapshot)]);
1867 }
1868 }
1869 }
1870 }
1871
1872 fn unregister_buffer_from_language_server(
1873 &mut self,
1874 buffer: &ModelHandle<Buffer>,
1875 old_path: PathBuf,
1876 cx: &mut ModelContext<Self>,
1877 ) {
1878 buffer.update(cx, |buffer, cx| {
1879 buffer.update_diagnostics(Default::default(), cx);
1880 self.buffer_snapshots.remove(&buffer.remote_id());
1881 if let Some((_, language_server)) = self.language_server_for_buffer(buffer, cx) {
1882 language_server
1883 .notify::<lsp::notification::DidCloseTextDocument>(
1884 lsp::DidCloseTextDocumentParams {
1885 text_document: lsp::TextDocumentIdentifier::new(
1886 lsp::Url::from_file_path(old_path).unwrap(),
1887 ),
1888 },
1889 )
1890 .log_err();
1891 }
1892 });
1893 }
1894
1895 fn on_buffer_event(
1896 &mut self,
1897 buffer: ModelHandle<Buffer>,
1898 event: &BufferEvent,
1899 cx: &mut ModelContext<'_, Self>,
1900 ) -> Option<()> {
1901 match event {
1902 BufferEvent::Operation(operation) => {
1903 if let Some(project_id) = self.shared_remote_id() {
1904 let request = self.client.request(proto::UpdateBuffer {
1905 project_id,
1906 buffer_id: buffer.read(cx).remote_id(),
1907 operations: vec![language::proto::serialize_operation(&operation)],
1908 });
1909 cx.background().spawn(request).detach_and_log_err(cx);
1910 } else if let Some(project_id) = self.remote_id() {
1911 let _ = self
1912 .client
1913 .send(proto::RegisterProjectActivity { project_id });
1914 }
1915 }
1916 BufferEvent::Edited { .. } => {
1917 let language_server = self
1918 .language_server_for_buffer(buffer.read(cx), cx)
1919 .map(|(_, server)| server.clone())?;
1920 let buffer = buffer.read(cx);
1921 let file = File::from_dyn(buffer.file())?;
1922 let abs_path = file.as_local()?.abs_path(cx);
1923 let uri = lsp::Url::from_file_path(abs_path).unwrap();
1924 let buffer_snapshots = self.buffer_snapshots.get_mut(&buffer.remote_id())?;
1925 let (version, prev_snapshot) = buffer_snapshots.last()?;
1926 let next_snapshot = buffer.text_snapshot();
1927 let next_version = version + 1;
1928
1929 let content_changes = buffer
1930 .edits_since::<(PointUtf16, usize)>(prev_snapshot.version())
1931 .map(|edit| {
1932 let edit_start = edit.new.start.0;
1933 let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
1934 let new_text = next_snapshot
1935 .text_for_range(edit.new.start.1..edit.new.end.1)
1936 .collect();
1937 lsp::TextDocumentContentChangeEvent {
1938 range: Some(lsp::Range::new(
1939 point_to_lsp(edit_start),
1940 point_to_lsp(edit_end),
1941 )),
1942 range_length: None,
1943 text: new_text,
1944 }
1945 })
1946 .collect();
1947
1948 buffer_snapshots.push((next_version, next_snapshot));
1949
1950 language_server
1951 .notify::<lsp::notification::DidChangeTextDocument>(
1952 lsp::DidChangeTextDocumentParams {
1953 text_document: lsp::VersionedTextDocumentIdentifier::new(
1954 uri,
1955 next_version,
1956 ),
1957 content_changes,
1958 },
1959 )
1960 .log_err();
1961 }
1962 BufferEvent::Saved => {
1963 let file = File::from_dyn(buffer.read(cx).file())?;
1964 let worktree_id = file.worktree_id(cx);
1965 let abs_path = file.as_local()?.abs_path(cx);
1966 let text_document = lsp::TextDocumentIdentifier {
1967 uri: lsp::Url::from_file_path(abs_path).unwrap(),
1968 };
1969
1970 for (_, server) in self.language_servers_for_worktree(worktree_id) {
1971 server
1972 .notify::<lsp::notification::DidSaveTextDocument>(
1973 lsp::DidSaveTextDocumentParams {
1974 text_document: text_document.clone(),
1975 text: None,
1976 },
1977 )
1978 .log_err();
1979 }
1980
1981 // After saving a buffer, simulate disk-based diagnostics being finished for languages
1982 // that don't support a disk-based progress token.
1983 let (lsp_adapter, language_server) =
1984 self.language_server_for_buffer(buffer.read(cx), cx)?;
1985 if lsp_adapter.disk_based_diagnostics_progress_token.is_none() {
1986 let server_id = language_server.server_id();
1987 self.disk_based_diagnostics_finished(server_id, cx);
1988 self.broadcast_language_server_update(
1989 server_id,
1990 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
1991 proto::LspDiskBasedDiagnosticsUpdated {},
1992 ),
1993 );
1994 }
1995 }
1996 _ => {}
1997 }
1998
1999 None
2000 }
2001
2002 fn language_servers_for_worktree(
2003 &self,
2004 worktree_id: WorktreeId,
2005 ) -> impl Iterator<Item = &(Arc<LspAdapter>, Arc<LanguageServer>)> {
2006 self.language_servers.iter().filter_map(
2007 move |((language_server_worktree_id, _), server)| {
2008 if *language_server_worktree_id == worktree_id {
2009 if let Some(LanguageServerState::Running { adapter, server }) =
2010 self.language_servers.get(&id)
2011 {
2012 return Some((adapter, server));
2013 }
2014 }
2015 None
2016 })
2017 }
2018
2019 fn assign_language_to_buffer(
2020 &mut self,
2021 buffer: &ModelHandle<Buffer>,
2022 cx: &mut ModelContext<Self>,
2023 ) -> Option<()> {
2024 // If the buffer has a language, set it and start the language server if we haven't already.
2025 let full_path = buffer.read(cx).file()?.full_path(cx);
2026 let language = self.languages.select_language(&full_path)?;
2027 buffer.update(cx, |buffer, cx| {
2028 buffer.set_language(Some(language.clone()), cx);
2029 });
2030
2031 let file = File::from_dyn(buffer.read(cx).file())?;
2032 let worktree = file.worktree.read(cx).as_local()?;
2033 let worktree_id = worktree.id();
2034 let worktree_abs_path = worktree.abs_path().clone();
2035 self.start_language_server(worktree_id, worktree_abs_path, language, cx);
2036
2037 None
2038 }
2039
2040 fn start_language_server(
2041 &mut self,
2042 worktree_id: WorktreeId,
2043 worktree_path: Arc<Path>,
2044 language: Arc<Language>,
2045 cx: &mut ModelContext<'_, Self>,
2046 ) {
2047 if !cx
2048 .global::<Settings>()
2049 .enable_language_server(Some(&language.name()))
2050 {
2051 return;
2052 }
2053
2054 let adapter = if let Some(adapter) = language.lsp_adapter() {
2055 adapter
2056 } else {
2057 return;
2058 };
2059
2060 let key = (worktree_id, adapter.name);
2061 self.language_server_ids
2062 .entry(key.clone())
2063 .or_insert_with(|| {
2064 let server_id = post_inc(&mut self.next_language_server_id);
2065 let language_server = self.languages.start_language_server(
2066 server_id,
2067 language.clone(),
2068 worktree_path,
2069 self.client.http_client(),
2070 cx,
2071 );
2072
2073 self.language_servers.insert(
2074 server_id,
2075 LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
2076 let language_server = language_server?.await.log_err()?;
2077 let language_server = language_server
2078 .initialize(adapter.initialization_options())
2079 .await
2080 .log_err()?;
2081 let this = this.upgrade(&cx)?;
2082 let disk_based_diagnostics_progress_token =
2083 adapter.disk_based_diagnostics_progress_token();
2084
2085 language_server
2086 .on_notification::<lsp::notification::PublishDiagnostics, _>({
2087 let this = this.downgrade();
2088 let adapter = adapter.clone();
2089 move |params, mut cx| {
2090 if let Some(this) = this.upgrade(&cx) {
2091 this.update(&mut cx, |this, cx| {
2092 // TODO(isaac): remove block on
2093 smol::block_on(this.on_lsp_diagnostics_published(
2094 server_id, params, &adapter, cx,
2095 ))
2096 });
2097 }
2098 }
2099 })
2100 .detach();
2101
2102 language_server
2103 .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
2104 let settings = this.read_with(&cx, |this, _| {
2105 this.language_server_settings.clone()
2106 });
2107 move |params, _| {
2108 let settings = settings.lock().clone();
2109 async move {
2110 Ok(params
2111 .items
2112 .into_iter()
2113 .map(|item| {
2114 if let Some(section) = &item.section {
2115 settings
2116 .get(section)
2117 .cloned()
2118 .unwrap_or(serde_json::Value::Null)
2119 } else {
2120 settings.clone()
2121 }
2122 })
2123 .collect())
2124 }
2125 }
2126 })
2127 .detach();
2128
2129 // Even though we don't have handling for these requests, respond to them to
2130 // avoid stalling any language server like `gopls` which waits for a response
2131 // to these requests when initializing.
2132 language_server
2133 .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
2134 let this = this.downgrade();
2135 move |params, mut cx| async move {
2136 if let Some(this) = this.upgrade(&cx) {
2137 this.update(&mut cx, |this, _| {
2138 if let Some(status) =
2139 this.language_server_statuses.get_mut(&server_id)
2140 {
2141 if let lsp::NumberOrString::String(token) =
2142 params.token
2143 {
2144 status.progress_tokens.insert(token);
2145 }
2146 }
2147 });
2148 }
2149 Ok(())
2150 }
2151 })
2152 .detach();
2153 language_server
2154 .on_request::<lsp::request::RegisterCapability, _, _>(|_, _| async {
2155 Ok(())
2156 })
2157 .detach();
2158
2159 language_server
2160 .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
2161 let this = this.downgrade();
2162 let adapter = adapter.clone();
2163 let language_server = language_server.clone();
2164 move |params, cx| {
2165 Self::on_lsp_workspace_edit(
2166 this,
2167 params,
2168 server_id,
2169 adapter.clone(),
2170 language_server.clone(),
2171 cx,
2172 )
2173
2174 }
2175 })
2176 .detach();
2177
2178 language_server
2179 .on_notification::<lsp::notification::Progress, _>({
2180 let this = this.downgrade();
2181 move |params, mut cx| {
2182 if let Some(this) = this.upgrade(&cx) {
2183 this.update(&mut cx, |this, cx| {
2184 this.on_lsp_progress(
2185 params,
2186 server_id,
2187 disk_based_diagnostics_progress_token,
2188 cx,
2189 );
2190 });
2191 }
2192 }
2193 })
2194 .detach();
2195
2196 this.update(&mut cx, |this, cx| {
2197 // If the language server for this key doesn't match the server id, don't store the
2198 // server. Which will cause it to be dropped, killing the process
2199 if this
2200 .language_server_ids
2201 .get(&key)
2202 .map(|id| id != &server_id)
2203 .unwrap_or(false)
2204 {
2205 return None;
2206 }
2207
2208 // Update language_servers collection with Running variant of LanguageServerState
2209 // indicating that the server is up and running and ready
2210 this.language_servers.insert(
2211 server_id,
2212 LanguageServerState::Running {
2213 adapter: adapter.clone(),
2214 server: language_server.clone(),
2215 },
2216 );
2217 this.language_server_statuses.insert(
2218 server_id,
2219 LanguageServerStatus {
2220 name: language_server.name().to_string(),
2221 pending_work: Default::default(),
2222 has_pending_diagnostic_updates: false,
2223 progress_tokens: Default::default(),
2224 },
2225 );
2226 language_server
2227 .notify::<lsp::notification::DidChangeConfiguration>(
2228 lsp::DidChangeConfigurationParams {
2229 settings: this.language_server_settings.lock().clone(),
2230 },
2231 )
2232 .ok();
2233
2234 if let Some(project_id) = this.shared_remote_id() {
2235 this.client
2236 .send(proto::StartLanguageServer {
2237 project_id,
2238 server: Some(proto::LanguageServer {
2239 id: server_id as u64,
2240 name: language_server.name().to_string(),
2241 }),
2242 })
2243 .log_err();
2244 }
2245
2246 // Tell the language server about every open buffer in the worktree that matches the language.
2247 for buffer in this.opened_buffers.values() {
2248 if let Some(buffer_handle) = buffer.upgrade(cx) {
2249 let buffer = buffer_handle.read(cx);
2250 let file = if let Some(file) = File::from_dyn(buffer.file()) {
2251 file
2252 } else {
2253 continue;
2254 };
2255 let language = if let Some(language) = buffer.language() {
2256 language
2257 } else {
2258 continue;
2259 };
2260 if file.worktree.read(cx).id() != key.0
2261 || language.lsp_adapter().map(|a| a.name())
2262 != Some(key.1.clone())
2263 {
2264 continue;
2265 }
2266
2267 let file = file.as_local()?;
2268 let versions = this
2269 .buffer_snapshots
2270 .entry(buffer.remote_id())
2271 .or_insert_with(|| vec![(0, buffer.text_snapshot())]);
2272 let (version, initial_snapshot) = versions.last().unwrap();
2273 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
2274 let language_id =
2275 adapter.id_for_language(language.name().as_ref());
2276 language_server
2277 .notify::<lsp::notification::DidOpenTextDocument>(
2278 lsp::DidOpenTextDocumentParams {
2279 text_document: lsp::TextDocumentItem::new(
2280 uri,
2281 language_id.unwrap_or_default(),
2282 *version,
2283 initial_snapshot.text(),
2284 ),
2285 },
2286 )
2287 .log_err()?;
2288 buffer_handle.update(cx, |buffer, cx| {
2289 buffer.set_completion_triggers(
2290 language_server
2291 .capabilities()
2292 .completion_provider
2293 .as_ref()
2294 .and_then(|provider| {
2295 provider.trigger_characters.clone()
2296 })
2297 .unwrap_or(Vec::new()),
2298 cx,
2299 )
2300 });
2301 }
2302 }
2303
2304 cx.notify();
2305 Some(language_server)
2306 })
2307 })),
2308 );
2309 });
2310 }
2311
2312 // Returns a list of all of the worktrees which no longer have a language server and the root path
2313 // for the stopped server
2314 fn stop_language_server(
2315 &mut self,
2316 worktree_id: WorktreeId,
2317 adapter_name: LanguageServerName,
2318 cx: &mut ModelContext<Self>,
2319 ) -> Task<(Option<PathBuf>, Vec<WorktreeId>)> {
2320 let key = (worktree_id, adapter_name);
2321 if let Some(server_id) = self.language_server_ids.remove(&key) {
2322 // Remove other entries for this language server as well
2323 let mut orphaned_worktrees = vec![worktree_id];
2324 let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
2325 for other_key in other_keys {
2326 if self.language_server_ids.get(&other_key) == Some(&server_id) {
2327 self.language_server_ids.remove(&other_key);
2328 orphaned_worktrees.push(other_key.0);
2329 }
2330 }
2331
2332 self.language_server_statuses.remove(&server_id);
2333 cx.notify();
2334
2335 let server_state = self.language_servers.remove(&server_id);
2336 cx.spawn_weak(|this, mut cx| async move {
2337 let mut root_path = None;
2338
2339 let server = match server_state {
2340 Some(LanguageServerState::Starting(started_language_server)) => {
2341 started_language_server.await
2342 }
2343 Some(LanguageServerState::Running { server, .. }) => Some(server),
2344 None => None,
2345 };
2346
2347 if let Some(server) = server {
2348 root_path = Some(server.root_path().clone());
2349 if let Some(shutdown) = server.shutdown() {
2350 shutdown.await;
2351 }
2352 }
2353
2354 if let Some(this) = this.upgrade(&cx) {
2355 this.update(&mut cx, |this, cx| {
2356 this.language_server_statuses.remove(&server_id);
2357 cx.notify();
2358 });
2359 }
2360
2361 (root_path, orphaned_worktrees)
2362 })
2363 } else {
2364 Task::ready((None, Vec::new()))
2365 }
2366 }
2367
2368 pub fn restart_language_servers_for_buffers(
2369 &mut self,
2370 buffers: impl IntoIterator<Item = ModelHandle<Buffer>>,
2371 cx: &mut ModelContext<Self>,
2372 ) -> Option<()> {
2373 let language_server_lookup_info: HashSet<(WorktreeId, Arc<Path>, PathBuf)> = buffers
2374 .into_iter()
2375 .filter_map(|buffer| {
2376 let file = File::from_dyn(buffer.read(cx).file())?;
2377 let worktree = file.worktree.read(cx).as_local()?;
2378 let worktree_id = worktree.id();
2379 let worktree_abs_path = worktree.abs_path().clone();
2380 let full_path = file.full_path(cx);
2381 Some((worktree_id, worktree_abs_path, full_path))
2382 })
2383 .collect();
2384 for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info {
2385 let language = self.languages.select_language(&full_path)?;
2386 self.restart_language_server(worktree_id, worktree_abs_path, language, cx);
2387 }
2388
2389 None
2390 }
2391
2392 fn restart_language_server(
2393 &mut self,
2394 worktree_id: WorktreeId,
2395 fallback_path: Arc<Path>,
2396 language: Arc<Language>,
2397 cx: &mut ModelContext<'_, Self>,
2398 ) {
2399 let adapter = if let Some(adapter) = language.lsp_adapter() {
2400 adapter
2401 } else {
2402 return;
2403 };
2404
2405 let stop = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
2406 cx.spawn_weak(|this, mut cx| async move {
2407 let (original_root_path, orphaned_worktrees) = stop.await;
2408 if let Some(this) = this.upgrade(&cx) {
2409 this.update(&mut cx, |this, cx| {
2410 // Attempt to restart using original server path. Fallback to passed in
2411 // path if we could not retrieve the root path
2412 let root_path = original_root_path
2413 .map(|path_buf| Arc::from(path_buf.as_path()))
2414 .unwrap_or(fallback_path);
2415
2416 this.start_language_server(worktree_id, root_path, language, cx);
2417
2418 // Lookup new server id and set it for each of the orphaned worktrees
2419 if let Some(new_server_id) = this
2420 .language_server_ids
2421 .get(&(worktree_id, server_name.clone()))
2422 .cloned()
2423 {
2424 for orphaned_worktree in orphaned_worktrees {
2425 this.language_server_ids.insert(
2426 (orphaned_worktree, server_name.clone()),
2427 new_server_id.clone(),
2428 );
2429 }
2430 }
2431 });
2432 }
2433 })
2434 .detach();
2435 }
2436
2437 async fn on_lsp_diagnostics_published(
2438 &mut self,
2439 server_id: usize,
2440 mut params: lsp::PublishDiagnosticsParams,
2441 adapter: &Arc<LspAdapter>,
2442 cx: &mut ModelContext<'_, Self>,
2443 ) {
2444 adapter.process_diagnostics(&mut params).await;
2445 self.update_diagnostics(
2446 server_id,
2447 params,
2448 &adapter.disk_based_diagnostic_sources,
2449 cx,
2450 )
2451 .log_err();
2452 }
2453
2454 fn on_lsp_progress(
2455 &mut self,
2456 progress: lsp::ProgressParams,
2457 server_id: usize,
2458 disk_based_diagnostics_progress_token: Option<&str>,
2459 cx: &mut ModelContext<Self>,
2460 ) {
2461 let token = match progress.token {
2462 lsp::NumberOrString::String(token) => token,
2463 lsp::NumberOrString::Number(token) => {
2464 log::info!("skipping numeric progress token {}", token);
2465 return;
2466 }
2467 };
2468 let progress = match progress.value {
2469 lsp::ProgressParamsValue::WorkDone(value) => value,
2470 };
2471 let language_server_status =
2472 if let Some(status) = self.language_server_statuses.get_mut(&server_id) {
2473 status
2474 } else {
2475 return;
2476 };
2477
2478 if !language_server_status.progress_tokens.contains(&token) {
2479 return;
2480 }
2481
2482 let same_token = Some(token.as_ref()) == disk_based_diagnostics_progress_token;
2483
2484 match progress {
2485 lsp::WorkDoneProgress::Begin(report) => {
2486 if same_token {
2487 language_server_status.has_pending_diagnostic_updates = true;
2488 self.disk_based_diagnostics_started(server_id, cx);
2489 self.broadcast_language_server_update(
2490 server_id,
2491 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
2492 proto::LspDiskBasedDiagnosticsUpdating {},
2493 ),
2494 );
2495 } else {
2496 self.on_lsp_work_start(
2497 server_id,
2498 token.clone(),
2499 LanguageServerProgress {
2500 message: report.message.clone(),
2501 percentage: report.percentage.map(|p| p as usize),
2502 last_update_at: Instant::now(),
2503 },
2504 cx,
2505 );
2506 self.broadcast_language_server_update(
2507 server_id,
2508 proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
2509 token,
2510 message: report.message,
2511 percentage: report.percentage.map(|p| p as u32),
2512 }),
2513 );
2514 }
2515 }
2516 lsp::WorkDoneProgress::Report(report) => {
2517 if !same_token {
2518 self.on_lsp_work_progress(
2519 server_id,
2520 token.clone(),
2521 LanguageServerProgress {
2522 message: report.message.clone(),
2523 percentage: report.percentage.map(|p| p as usize),
2524 last_update_at: Instant::now(),
2525 },
2526 cx,
2527 );
2528 self.broadcast_language_server_update(
2529 server_id,
2530 proto::update_language_server::Variant::WorkProgress(
2531 proto::LspWorkProgress {
2532 token,
2533 message: report.message,
2534 percentage: report.percentage.map(|p| p as u32),
2535 },
2536 ),
2537 );
2538 }
2539 }
2540 lsp::WorkDoneProgress::End(_) => {
2541 language_server_status.progress_tokens.remove(&token);
2542
2543 if same_token {
2544 language_server_status.has_pending_diagnostic_updates = false;
2545 self.disk_based_diagnostics_finished(server_id, cx);
2546 self.broadcast_language_server_update(
2547 server_id,
2548 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
2549 proto::LspDiskBasedDiagnosticsUpdated {},
2550 ),
2551 );
2552 } else {
2553 self.on_lsp_work_end(server_id, token.clone(), cx);
2554 self.broadcast_language_server_update(
2555 server_id,
2556 proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd {
2557 token,
2558 }),
2559 );
2560 }
2561 }
2562 }
2563 }
2564
2565 fn on_lsp_work_start(
2566 &mut self,
2567 language_server_id: usize,
2568 token: String,
2569 progress: LanguageServerProgress,
2570 cx: &mut ModelContext<Self>,
2571 ) {
2572 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
2573 status.pending_work.insert(token, progress);
2574 cx.notify();
2575 }
2576 }
2577
2578 fn on_lsp_work_progress(
2579 &mut self,
2580 language_server_id: usize,
2581 token: String,
2582 progress: LanguageServerProgress,
2583 cx: &mut ModelContext<Self>,
2584 ) {
2585 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
2586 let entry = status
2587 .pending_work
2588 .entry(token)
2589 .or_insert(LanguageServerProgress {
2590 message: Default::default(),
2591 percentage: Default::default(),
2592 last_update_at: progress.last_update_at,
2593 });
2594 if progress.message.is_some() {
2595 entry.message = progress.message;
2596 }
2597 if progress.percentage.is_some() {
2598 entry.percentage = progress.percentage;
2599 }
2600 entry.last_update_at = progress.last_update_at;
2601 cx.notify();
2602 }
2603 }
2604
2605 fn on_lsp_work_end(
2606 &mut self,
2607 language_server_id: usize,
2608 token: String,
2609 cx: &mut ModelContext<Self>,
2610 ) {
2611 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
2612 status.pending_work.remove(&token);
2613 cx.notify();
2614 }
2615 }
2616
2617 async fn on_lsp_workspace_edit(
2618 this: WeakModelHandle<Self>,
2619 params: lsp::ApplyWorkspaceEditParams,
2620 server_id: usize,
2621 adapter: Arc<LspAdapter>,
2622 language_server: Arc<LanguageServer>,
2623 mut cx: AsyncAppContext,
2624 ) -> Result<lsp::ApplyWorkspaceEditResponse> {
2625 let this = this
2626 .upgrade(&cx)
2627 .ok_or_else(|| anyhow!("project project closed"))?;
2628 let transaction = Self::deserialize_workspace_edit(
2629 this.clone(),
2630 params.edit,
2631 true,
2632 adapter.clone(),
2633 language_server.clone(),
2634 &mut cx,
2635 )
2636 .await
2637 .log_err();
2638 this.update(&mut cx, |this, _| {
2639 if let Some(transaction) = transaction {
2640 this.last_workspace_edits_by_language_server
2641 .insert(server_id, transaction);
2642 }
2643 });
2644 Ok(lsp::ApplyWorkspaceEditResponse {
2645 applied: true,
2646 failed_change: None,
2647 failure_reason: None,
2648 })
2649 }
2650
2651 fn broadcast_language_server_update(
2652 &self,
2653 language_server_id: usize,
2654 event: proto::update_language_server::Variant,
2655 ) {
2656 if let Some(project_id) = self.shared_remote_id() {
2657 self.client
2658 .send(proto::UpdateLanguageServer {
2659 project_id,
2660 language_server_id: language_server_id as u64,
2661 variant: Some(event),
2662 })
2663 .log_err();
2664 }
2665 }
2666
2667 pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
2668 for server_state in self.language_servers.values() {
2669 if let LanguageServerState::Running { server, .. } = server_state {
2670 server
2671 .notify::<lsp::notification::DidChangeConfiguration>(
2672 lsp::DidChangeConfigurationParams {
2673 settings: settings.clone(),
2674 },
2675 )
2676 .ok();
2677 }
2678 }
2679 *self.language_server_settings.lock() = settings;
2680 }
2681
2682 pub fn language_server_statuses(
2683 &self,
2684 ) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
2685 self.language_server_statuses.values()
2686 }
2687
2688 pub fn update_diagnostics(
2689 &mut self,
2690 language_server_id: usize,
2691 params: lsp::PublishDiagnosticsParams,
2692 disk_based_sources: &[String],
2693 cx: &mut ModelContext<Self>,
2694 ) -> Result<()> {
2695 let abs_path = params
2696 .uri
2697 .to_file_path()
2698 .map_err(|_| anyhow!("URI is not a file"))?;
2699 let mut diagnostics = Vec::default();
2700 let mut primary_diagnostic_group_ids = HashMap::default();
2701 let mut sources_by_group_id = HashMap::default();
2702 let mut supporting_diagnostics = HashMap::default();
2703 for diagnostic in ¶ms.diagnostics {
2704 let source = diagnostic.source.as_ref();
2705 let code = diagnostic.code.as_ref().map(|code| match code {
2706 lsp::NumberOrString::Number(code) => code.to_string(),
2707 lsp::NumberOrString::String(code) => code.clone(),
2708 });
2709 let range = range_from_lsp(diagnostic.range);
2710 let is_supporting = diagnostic
2711 .related_information
2712 .as_ref()
2713 .map_or(false, |infos| {
2714 infos.iter().any(|info| {
2715 primary_diagnostic_group_ids.contains_key(&(
2716 source,
2717 code.clone(),
2718 range_from_lsp(info.location.range),
2719 ))
2720 })
2721 });
2722
2723 let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
2724 tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
2725 });
2726
2727 if is_supporting {
2728 supporting_diagnostics.insert(
2729 (source, code.clone(), range),
2730 (diagnostic.severity, is_unnecessary),
2731 );
2732 } else {
2733 let group_id = post_inc(&mut self.next_diagnostic_group_id);
2734 let is_disk_based =
2735 source.map_or(false, |source| disk_based_sources.contains(&source));
2736
2737 sources_by_group_id.insert(group_id, source);
2738 primary_diagnostic_group_ids
2739 .insert((source, code.clone(), range.clone()), group_id);
2740
2741 diagnostics.push(DiagnosticEntry {
2742 range,
2743 diagnostic: Diagnostic {
2744 code: code.clone(),
2745 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
2746 message: diagnostic.message.clone(),
2747 group_id,
2748 is_primary: true,
2749 is_valid: true,
2750 is_disk_based,
2751 is_unnecessary,
2752 },
2753 });
2754 if let Some(infos) = &diagnostic.related_information {
2755 for info in infos {
2756 if info.location.uri == params.uri && !info.message.is_empty() {
2757 let range = range_from_lsp(info.location.range);
2758 diagnostics.push(DiagnosticEntry {
2759 range,
2760 diagnostic: Diagnostic {
2761 code: code.clone(),
2762 severity: DiagnosticSeverity::INFORMATION,
2763 message: info.message.clone(),
2764 group_id,
2765 is_primary: false,
2766 is_valid: true,
2767 is_disk_based,
2768 is_unnecessary: false,
2769 },
2770 });
2771 }
2772 }
2773 }
2774 }
2775 }
2776
2777 for entry in &mut diagnostics {
2778 let diagnostic = &mut entry.diagnostic;
2779 if !diagnostic.is_primary {
2780 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
2781 if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
2782 source,
2783 diagnostic.code.clone(),
2784 entry.range.clone(),
2785 )) {
2786 if let Some(severity) = severity {
2787 diagnostic.severity = severity;
2788 }
2789 diagnostic.is_unnecessary = is_unnecessary;
2790 }
2791 }
2792 }
2793
2794 self.update_diagnostic_entries(
2795 language_server_id,
2796 abs_path,
2797 params.version,
2798 diagnostics,
2799 cx,
2800 )?;
2801 Ok(())
2802 }
2803
2804 pub fn update_diagnostic_entries(
2805 &mut self,
2806 language_server_id: usize,
2807 abs_path: PathBuf,
2808 version: Option<i32>,
2809 diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
2810 cx: &mut ModelContext<Project>,
2811 ) -> Result<(), anyhow::Error> {
2812 let (worktree, relative_path) = self
2813 .find_local_worktree(&abs_path, cx)
2814 .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
2815
2816 let project_path = ProjectPath {
2817 worktree_id: worktree.read(cx).id(),
2818 path: relative_path.into(),
2819 };
2820 if let Some(buffer) = self.get_open_buffer(&project_path, cx) {
2821 self.update_buffer_diagnostics(&buffer, diagnostics.clone(), version, cx)?;
2822 }
2823
2824 let updated = worktree.update(cx, |worktree, cx| {
2825 worktree
2826 .as_local_mut()
2827 .ok_or_else(|| anyhow!("not a local worktree"))?
2828 .update_diagnostics(
2829 language_server_id,
2830 project_path.path.clone(),
2831 diagnostics,
2832 cx,
2833 )
2834 })?;
2835 if updated {
2836 cx.emit(Event::DiagnosticsUpdated {
2837 language_server_id,
2838 path: project_path,
2839 });
2840 }
2841 Ok(())
2842 }
2843
2844 fn update_buffer_diagnostics(
2845 &mut self,
2846 buffer: &ModelHandle<Buffer>,
2847 mut diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
2848 version: Option<i32>,
2849 cx: &mut ModelContext<Self>,
2850 ) -> Result<()> {
2851 fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
2852 Ordering::Equal
2853 .then_with(|| b.is_primary.cmp(&a.is_primary))
2854 .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
2855 .then_with(|| a.severity.cmp(&b.severity))
2856 .then_with(|| a.message.cmp(&b.message))
2857 }
2858
2859 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, version, cx)?;
2860
2861 diagnostics.sort_unstable_by(|a, b| {
2862 Ordering::Equal
2863 .then_with(|| a.range.start.cmp(&b.range.start))
2864 .then_with(|| b.range.end.cmp(&a.range.end))
2865 .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
2866 });
2867
2868 let mut sanitized_diagnostics = Vec::new();
2869 let edits_since_save = Patch::new(
2870 snapshot
2871 .edits_since::<PointUtf16>(buffer.read(cx).saved_version())
2872 .collect(),
2873 );
2874 for entry in diagnostics {
2875 let start;
2876 let end;
2877 if entry.diagnostic.is_disk_based {
2878 // Some diagnostics are based on files on disk instead of buffers'
2879 // current contents. Adjust these diagnostics' ranges to reflect
2880 // any unsaved edits.
2881 start = edits_since_save.old_to_new(entry.range.start);
2882 end = edits_since_save.old_to_new(entry.range.end);
2883 } else {
2884 start = entry.range.start;
2885 end = entry.range.end;
2886 }
2887
2888 let mut range = snapshot.clip_point_utf16(start, Bias::Left)
2889 ..snapshot.clip_point_utf16(end, Bias::Right);
2890
2891 // Expand empty ranges by one character
2892 if range.start == range.end {
2893 range.end.column += 1;
2894 range.end = snapshot.clip_point_utf16(range.end, Bias::Right);
2895 if range.start == range.end && range.end.column > 0 {
2896 range.start.column -= 1;
2897 range.start = snapshot.clip_point_utf16(range.start, Bias::Left);
2898 }
2899 }
2900
2901 sanitized_diagnostics.push(DiagnosticEntry {
2902 range,
2903 diagnostic: entry.diagnostic,
2904 });
2905 }
2906 drop(edits_since_save);
2907
2908 let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
2909 buffer.update(cx, |buffer, cx| buffer.update_diagnostics(set, cx));
2910 Ok(())
2911 }
2912
2913 pub fn reload_buffers(
2914 &self,
2915 buffers: HashSet<ModelHandle<Buffer>>,
2916 push_to_history: bool,
2917 cx: &mut ModelContext<Self>,
2918 ) -> Task<Result<ProjectTransaction>> {
2919 let mut local_buffers = Vec::new();
2920 let mut remote_buffers = None;
2921 for buffer_handle in buffers {
2922 let buffer = buffer_handle.read(cx);
2923 if buffer.is_dirty() {
2924 if let Some(file) = File::from_dyn(buffer.file()) {
2925 if file.is_local() {
2926 local_buffers.push(buffer_handle);
2927 } else {
2928 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
2929 }
2930 }
2931 }
2932 }
2933
2934 let remote_buffers = self.remote_id().zip(remote_buffers);
2935 let client = self.client.clone();
2936
2937 cx.spawn(|this, mut cx| async move {
2938 let mut project_transaction = ProjectTransaction::default();
2939
2940 if let Some((project_id, remote_buffers)) = remote_buffers {
2941 let response = client
2942 .request(proto::ReloadBuffers {
2943 project_id,
2944 buffer_ids: remote_buffers
2945 .iter()
2946 .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
2947 .collect(),
2948 })
2949 .await?
2950 .transaction
2951 .ok_or_else(|| anyhow!("missing transaction"))?;
2952 project_transaction = this
2953 .update(&mut cx, |this, cx| {
2954 this.deserialize_project_transaction(response, push_to_history, cx)
2955 })
2956 .await?;
2957 }
2958
2959 for buffer in local_buffers {
2960 let transaction = buffer
2961 .update(&mut cx, |buffer, cx| buffer.reload(cx))
2962 .await?;
2963 buffer.update(&mut cx, |buffer, cx| {
2964 if let Some(transaction) = transaction {
2965 if !push_to_history {
2966 buffer.forget_transaction(transaction.id);
2967 }
2968 project_transaction.0.insert(cx.handle(), transaction);
2969 }
2970 });
2971 }
2972
2973 Ok(project_transaction)
2974 })
2975 }
2976
2977 pub fn format(
2978 &self,
2979 buffers: HashSet<ModelHandle<Buffer>>,
2980 push_to_history: bool,
2981 cx: &mut ModelContext<Project>,
2982 ) -> Task<Result<ProjectTransaction>> {
2983 let mut local_buffers = Vec::new();
2984 let mut remote_buffers = None;
2985 for buffer_handle in buffers {
2986 let buffer = buffer_handle.read(cx);
2987 if let Some(file) = File::from_dyn(buffer.file()) {
2988 if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
2989 if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) {
2990 local_buffers.push((buffer_handle, buffer_abs_path, server.clone()));
2991 }
2992 } else {
2993 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
2994 }
2995 } else {
2996 return Task::ready(Ok(Default::default()));
2997 }
2998 }
2999
3000 let remote_buffers = self.remote_id().zip(remote_buffers);
3001 let client = self.client.clone();
3002
3003 cx.spawn(|this, mut cx| async move {
3004 let mut project_transaction = ProjectTransaction::default();
3005
3006 if let Some((project_id, remote_buffers)) = remote_buffers {
3007 let response = client
3008 .request(proto::FormatBuffers {
3009 project_id,
3010 buffer_ids: remote_buffers
3011 .iter()
3012 .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
3013 .collect(),
3014 })
3015 .await?
3016 .transaction
3017 .ok_or_else(|| anyhow!("missing transaction"))?;
3018 project_transaction = this
3019 .update(&mut cx, |this, cx| {
3020 this.deserialize_project_transaction(response, push_to_history, cx)
3021 })
3022 .await?;
3023 }
3024
3025 for (buffer, buffer_abs_path, language_server) in local_buffers {
3026 let (format_on_save, tab_size) = buffer.read_with(&cx, |buffer, cx| {
3027 let settings = cx.global::<Settings>();
3028 let language_name = buffer.language().map(|language| language.name());
3029 (
3030 settings.format_on_save(language_name.as_deref()),
3031 settings.tab_size(language_name.as_deref()),
3032 )
3033 });
3034
3035 let transaction = match format_on_save {
3036 settings::FormatOnSave::Off => continue,
3037 settings::FormatOnSave::LanguageServer => Self::format_via_lsp(
3038 &this,
3039 &buffer,
3040 &buffer_abs_path,
3041 &language_server,
3042 tab_size,
3043 &mut cx,
3044 )
3045 .await
3046 .context("failed to format via language server")?,
3047 settings::FormatOnSave::External { command, arguments } => {
3048 Self::format_via_external_command(
3049 &buffer,
3050 &buffer_abs_path,
3051 &command,
3052 &arguments,
3053 &mut cx,
3054 )
3055 .await
3056 .context(format!(
3057 "failed to format via external command {:?}",
3058 command
3059 ))?
3060 }
3061 };
3062
3063 if let Some(transaction) = transaction {
3064 if !push_to_history {
3065 buffer.update(&mut cx, |buffer, _| {
3066 buffer.forget_transaction(transaction.id)
3067 });
3068 }
3069 project_transaction.0.insert(buffer, transaction);
3070 }
3071 }
3072
3073 Ok(project_transaction)
3074 })
3075 }
3076
3077 async fn format_via_lsp(
3078 this: &ModelHandle<Self>,
3079 buffer: &ModelHandle<Buffer>,
3080 abs_path: &Path,
3081 language_server: &Arc<LanguageServer>,
3082 tab_size: NonZeroU32,
3083 cx: &mut AsyncAppContext,
3084 ) -> Result<Option<Transaction>> {
3085 let text_document =
3086 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(abs_path).unwrap());
3087 let capabilities = &language_server.capabilities();
3088 let lsp_edits = if capabilities
3089 .document_formatting_provider
3090 .as_ref()
3091 .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
3092 {
3093 language_server
3094 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
3095 text_document,
3096 options: lsp::FormattingOptions {
3097 tab_size: tab_size.into(),
3098 insert_spaces: true,
3099 insert_final_newline: Some(true),
3100 ..Default::default()
3101 },
3102 work_done_progress_params: Default::default(),
3103 })
3104 .await?
3105 } else if capabilities
3106 .document_range_formatting_provider
3107 .as_ref()
3108 .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
3109 {
3110 let buffer_start = lsp::Position::new(0, 0);
3111 let buffer_end =
3112 buffer.read_with(cx, |buffer, _| point_to_lsp(buffer.max_point_utf16()));
3113 language_server
3114 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
3115 text_document,
3116 range: lsp::Range::new(buffer_start, buffer_end),
3117 options: lsp::FormattingOptions {
3118 tab_size: tab_size.into(),
3119 insert_spaces: true,
3120 insert_final_newline: Some(true),
3121 ..Default::default()
3122 },
3123 work_done_progress_params: Default::default(),
3124 })
3125 .await?
3126 } else {
3127 None
3128 };
3129
3130 if let Some(lsp_edits) = lsp_edits {
3131 let edits = this
3132 .update(cx, |this, cx| {
3133 this.edits_from_lsp(&buffer, lsp_edits, None, cx)
3134 })
3135 .await?;
3136 buffer.update(cx, |buffer, cx| {
3137 buffer.finalize_last_transaction();
3138 buffer.start_transaction();
3139 for (range, text) in edits {
3140 buffer.edit([(range, text)], cx);
3141 }
3142 if buffer.end_transaction(cx).is_some() {
3143 let transaction = buffer.finalize_last_transaction().unwrap().clone();
3144 Ok(Some(transaction))
3145 } else {
3146 Ok(None)
3147 }
3148 })
3149 } else {
3150 Ok(None)
3151 }
3152 }
3153
3154 async fn format_via_external_command(
3155 buffer: &ModelHandle<Buffer>,
3156 buffer_abs_path: &Path,
3157 command: &str,
3158 arguments: &[String],
3159 cx: &mut AsyncAppContext,
3160 ) -> Result<Option<Transaction>> {
3161 let working_dir_path = buffer.read_with(cx, |buffer, cx| {
3162 let file = File::from_dyn(buffer.file())?;
3163 let worktree = file.worktree.read(cx).as_local()?;
3164 let mut worktree_path = worktree.abs_path().to_path_buf();
3165 if worktree.root_entry()?.is_file() {
3166 worktree_path.pop();
3167 }
3168 Some(worktree_path)
3169 });
3170
3171 if let Some(working_dir_path) = working_dir_path {
3172 let mut child =
3173 smol::process::Command::new(command)
3174 .args(arguments.iter().map(|arg| {
3175 arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
3176 }))
3177 .current_dir(&working_dir_path)
3178 .stdin(smol::process::Stdio::piped())
3179 .stdout(smol::process::Stdio::piped())
3180 .stderr(smol::process::Stdio::piped())
3181 .spawn()?;
3182 let stdin = child
3183 .stdin
3184 .as_mut()
3185 .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
3186 let text = buffer.read_with(cx, |buffer, _| buffer.as_rope().clone());
3187 for chunk in text.chunks() {
3188 stdin.write_all(chunk.as_bytes()).await?;
3189 }
3190 stdin.flush().await?;
3191
3192 let output = child.output().await?;
3193 if !output.status.success() {
3194 return Err(anyhow!(
3195 "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
3196 output.status.code(),
3197 String::from_utf8_lossy(&output.stdout),
3198 String::from_utf8_lossy(&output.stderr),
3199 ));
3200 }
3201
3202 let stdout = String::from_utf8(output.stdout)?;
3203 let diff = buffer
3204 .read_with(cx, |buffer, cx| buffer.diff(stdout, cx))
3205 .await;
3206 Ok(buffer.update(cx, |buffer, cx| buffer.apply_diff(diff, cx).cloned()))
3207 } else {
3208 Ok(None)
3209 }
3210 }
3211
3212 pub fn definition<T: ToPointUtf16>(
3213 &self,
3214 buffer: &ModelHandle<Buffer>,
3215 position: T,
3216 cx: &mut ModelContext<Self>,
3217 ) -> Task<Result<Vec<LocationLink>>> {
3218 let position = position.to_point_utf16(buffer.read(cx));
3219 self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
3220 }
3221
3222 pub fn references<T: ToPointUtf16>(
3223 &self,
3224 buffer: &ModelHandle<Buffer>,
3225 position: T,
3226 cx: &mut ModelContext<Self>,
3227 ) -> Task<Result<Vec<Location>>> {
3228 let position = position.to_point_utf16(buffer.read(cx));
3229 self.request_lsp(buffer.clone(), GetReferences { position }, cx)
3230 }
3231
3232 pub fn document_highlights<T: ToPointUtf16>(
3233 &self,
3234 buffer: &ModelHandle<Buffer>,
3235 position: T,
3236 cx: &mut ModelContext<Self>,
3237 ) -> Task<Result<Vec<DocumentHighlight>>> {
3238 let position = position.to_point_utf16(buffer.read(cx));
3239
3240 self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
3241 }
3242
3243 pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
3244 if self.is_local() {
3245 let mut requests = Vec::new();
3246 for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
3247 let worktree_id = *worktree_id;
3248 if let Some(worktree) = self
3249 .worktree_for_id(worktree_id, cx)
3250 .and_then(|worktree| worktree.read(cx).as_local())
3251 {
3252 if let Some(LanguageServerState::Running { adapter, server }) =
3253 self.language_servers.get(server_id)
3254 {
3255 let adapter = adapter.clone();
3256 let worktree_abs_path = worktree.abs_path().clone();
3257 requests.push(
3258 server
3259 .request::<lsp::request::WorkspaceSymbol>(
3260 lsp::WorkspaceSymbolParams {
3261 query: query.to_string(),
3262 ..Default::default()
3263 },
3264 )
3265 .log_err()
3266 .map(move |response| {
3267 (
3268 adapter,
3269 worktree_id,
3270 worktree_abs_path,
3271 response.unwrap_or_default(),
3272 )
3273 }),
3274 );
3275 }
3276 }
3277 }
3278
3279 cx.spawn_weak(|this, cx| async move {
3280 let responses = futures::future::join_all(requests).await;
3281 let this = if let Some(this) = this.upgrade(&cx) {
3282 this
3283 } else {
3284 return Ok(Default::default());
3285 };
3286
3287 struct PartialSymbol {
3288 source_worktree_id: WorktreeId,
3289 worktree_id: WorktreeId,
3290 language_server_name: LanguageServerName,
3291 path: PathBuf,
3292 language: Option<Arc<Language>>,
3293 name: String,
3294 kind: lsp::SymbolKind,
3295 range: Range<PointUtf16>,
3296 signature: [u8; 32],
3297 }
3298
3299 let partial_symbols = this.read_with(&cx, |this, cx| {
3300 let mut partial_symbols = Vec::new();
3301 for (adapter, source_worktree_id, worktree_abs_path, response) in responses {
3302 for lsp_symbol in response.into_iter().flatten() {
3303 let abs_path = match lsp_symbol.location.uri.to_file_path().ok() {
3304 Some(abs_path) => abs_path,
3305 None => continue,
3306 };
3307 let mut worktree_id = source_worktree_id;
3308 let path;
3309 if let Some((worktree, rel_path)) =
3310 this.find_local_worktree(&abs_path, cx)
3311 {
3312 worktree_id = worktree.read(cx).id();
3313 path = rel_path;
3314 } else {
3315 path = relativize_path(&worktree_abs_path, &abs_path);
3316 }
3317
3318 let language = this.languages.select_language(&path).clone();
3319 let signature = this.symbol_signature(worktree_id, &path);
3320
3321 partial_symbols.push(PartialSymbol {
3322 source_worktree_id,
3323 worktree_id,
3324 language_server_name: adapter.name.clone(),
3325 name: lsp_symbol.name,
3326 kind: lsp_symbol.kind,
3327 language,
3328 path,
3329 range: range_from_lsp(lsp_symbol.location.range),
3330 signature,
3331 });
3332 }
3333 }
3334
3335 partial_symbols
3336 });
3337
3338 let mut symbols = Vec::new();
3339 for ps in partial_symbols.into_iter() {
3340 symbols.push(async move {
3341 let label = match ps.language {
3342 Some(language) => language.label_for_symbol(&ps.name, ps.kind).await,
3343 None => None,
3344 }
3345 .unwrap_or_else(|| CodeLabel::plain(ps.name.clone(), None));
3346
3347 Symbol {
3348 source_worktree_id: ps.source_worktree_id,
3349 worktree_id: ps.worktree_id,
3350 language_server_name: ps.language_server_name,
3351 name: ps.name,
3352 kind: ps.kind,
3353 label,
3354 path: ps.path,
3355 range: ps.range,
3356 signature: ps.signature,
3357 }
3358 });
3359 }
3360
3361 Ok(futures::future::join_all(symbols).await)
3362 })
3363 } else if let Some(project_id) = self.remote_id() {
3364 let request = self.client.request(proto::GetProjectSymbols {
3365 project_id,
3366 query: query.to_string(),
3367 });
3368 cx.spawn_weak(|this, cx| async move {
3369 let response = request.await?;
3370 let mut symbols = Vec::new();
3371 if let Some(this) = this.upgrade(&cx) {
3372 let new_symbols = this.read_with(&cx, |this, _| {
3373 let mut new_symbols = Vec::new();
3374 for symbol in response.symbols.into_iter() {
3375 new_symbols.push(this.deserialize_symbol(symbol));
3376 }
3377 new_symbols
3378 });
3379 for new_symbol in new_symbols {
3380 if let Some(new_symbol) = new_symbol.await.ok() {
3381 symbols.push(new_symbol);
3382 }
3383 }
3384 }
3385 Ok(symbols)
3386 })
3387 } else {
3388 Task::ready(Ok(Default::default()))
3389 }
3390 }
3391
3392 pub fn open_buffer_for_symbol(
3393 &mut self,
3394 symbol: &Symbol,
3395 cx: &mut ModelContext<Self>,
3396 ) -> Task<Result<ModelHandle<Buffer>>> {
3397 if self.is_local() {
3398 let language_server_id = if let Some(id) = self.language_server_ids.get(&(
3399 symbol.source_worktree_id,
3400 symbol.language_server_name.clone(),
3401 )) {
3402 *id
3403 } else {
3404 return Task::ready(Err(anyhow!(
3405 "language server for worktree and language not found"
3406 )));
3407 };
3408
3409 let worktree_abs_path = if let Some(worktree_abs_path) = self
3410 .worktree_for_id(symbol.worktree_id, cx)
3411 .and_then(|worktree| worktree.read(cx).as_local())
3412 .map(|local_worktree| local_worktree.abs_path())
3413 {
3414 worktree_abs_path
3415 } else {
3416 return Task::ready(Err(anyhow!("worktree not found for symbol")));
3417 };
3418 let symbol_abs_path = worktree_abs_path.join(&symbol.path);
3419 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
3420 uri
3421 } else {
3422 return Task::ready(Err(anyhow!("invalid symbol path")));
3423 };
3424
3425 self.open_local_buffer_via_lsp(
3426 symbol_uri,
3427 language_server_id,
3428 symbol.language_server_name.clone(),
3429 cx,
3430 )
3431 } else if let Some(project_id) = self.remote_id() {
3432 let request = self.client.request(proto::OpenBufferForSymbol {
3433 project_id,
3434 symbol: Some(serialize_symbol(symbol)),
3435 });
3436 cx.spawn(|this, mut cx| async move {
3437 let response = request.await?;
3438 let buffer = response.buffer.ok_or_else(|| anyhow!("invalid buffer"))?;
3439 this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
3440 .await
3441 })
3442 } else {
3443 Task::ready(Err(anyhow!("project does not have a remote id")))
3444 }
3445 }
3446
3447 pub fn hover<T: ToPointUtf16>(
3448 &self,
3449 buffer: &ModelHandle<Buffer>,
3450 position: T,
3451 cx: &mut ModelContext<Self>,
3452 ) -> Task<Result<Option<Hover>>> {
3453 let position = position.to_point_utf16(buffer.read(cx));
3454 self.request_lsp(buffer.clone(), GetHover { position }, cx)
3455 }
3456
3457 pub fn completions<T: ToPointUtf16>(
3458 &self,
3459 source_buffer_handle: &ModelHandle<Buffer>,
3460 position: T,
3461 cx: &mut ModelContext<Self>,
3462 ) -> Task<Result<Vec<Completion>>> {
3463 let source_buffer_handle = source_buffer_handle.clone();
3464 let source_buffer = source_buffer_handle.read(cx);
3465 let buffer_id = source_buffer.remote_id();
3466 let language = source_buffer.language().cloned();
3467 let worktree;
3468 let buffer_abs_path;
3469 if let Some(file) = File::from_dyn(source_buffer.file()) {
3470 worktree = file.worktree.clone();
3471 buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
3472 } else {
3473 return Task::ready(Ok(Default::default()));
3474 };
3475
3476 let position = position.to_point_utf16(source_buffer);
3477 let anchor = source_buffer.anchor_after(position);
3478
3479 if worktree.read(cx).as_local().is_some() {
3480 let buffer_abs_path = buffer_abs_path.unwrap();
3481 let lang_server =
3482 if let Some((_, server)) = self.language_server_for_buffer(source_buffer, cx) {
3483 server.clone()
3484 } else {
3485 return Task::ready(Ok(Default::default()));
3486 };
3487
3488 cx.spawn(|_, cx| async move {
3489 let completions = lang_server
3490 .request::<lsp::request::Completion>(lsp::CompletionParams {
3491 text_document_position: lsp::TextDocumentPositionParams::new(
3492 lsp::TextDocumentIdentifier::new(
3493 lsp::Url::from_file_path(buffer_abs_path).unwrap(),
3494 ),
3495 point_to_lsp(position),
3496 ),
3497 context: Default::default(),
3498 work_done_progress_params: Default::default(),
3499 partial_result_params: Default::default(),
3500 })
3501 .await
3502 .context("lsp completion request failed")?;
3503
3504 let completions = if let Some(completions) = completions {
3505 match completions {
3506 lsp::CompletionResponse::Array(completions) => completions,
3507 lsp::CompletionResponse::List(list) => list.items,
3508 }
3509 } else {
3510 Default::default()
3511 };
3512
3513 struct PartialCompletion {
3514 pub old_range: Range<Anchor>,
3515 pub new_text: String,
3516 pub language: Option<Arc<Language>>,
3517 pub lsp_completion: lsp::CompletionItem,
3518 }
3519
3520 let partial_completions = source_buffer_handle.read_with(&cx, |this, _| {
3521 let snapshot = this.snapshot();
3522 let clipped_position = this.clip_point_utf16(position, Bias::Left);
3523 let mut range_for_token = None;
3524 let mut partial_completions = Vec::new();
3525
3526 for lsp_completion in completions.into_iter() {
3527 // For now, we can only handle additional edits if they are returned
3528 // when resolving the completion, not if they are present initially.
3529 if lsp_completion
3530 .additional_text_edits
3531 .as_ref()
3532 .map_or(false, |edits| !edits.is_empty())
3533 {
3534 continue;
3535 }
3536 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref()
3537 {
3538 // If the language server provides a range to overwrite, then
3539 // check that the range is valid.
3540 Some(lsp::CompletionTextEdit::Edit(edit)) => {
3541 let range = range_from_lsp(edit.range);
3542 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
3543 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
3544 if start != range.start || end != range.end {
3545 log::info!("completion out of expected range");
3546 return None;
3547 }
3548 (
3549 snapshot.anchor_before(start)..snapshot.anchor_after(end),
3550 edit.new_text.clone(),
3551 )
3552 }
3553 // If the language server does not provide a range, then infer
3554 // the range based on the syntax tree.
3555 None => {
3556 if position != clipped_position {
3557 log::info!("completion out of expected range");
3558 return None;
3559 }
3560 let Range { start, end } = range_for_token
3561 .get_or_insert_with(|| {
3562 let offset = position.to_offset(&snapshot);
3563 let (range, kind) = snapshot.surrounding_word(offset);
3564 if kind == Some(CharKind::Word) {
3565 range
3566 } else {
3567 offset..offset
3568 }
3569 })
3570 .clone();
3571 let text = lsp_completion
3572 .insert_text
3573 .as_ref()
3574 .unwrap_or(&lsp_completion.label)
3575 .clone();
3576 (
3577 snapshot.anchor_before(start)..snapshot.anchor_after(end),
3578 text.clone(),
3579 )
3580 }
3581 (
3582 snapshot.anchor_before(start)..snapshot.anchor_after(end),
3583 edit.new_text.clone(),
3584 )
3585 }
3586 // If the language server does not provide a range, then infer
3587 // the range based on the syntax tree.
3588 None => {
3589 if position != clipped_position {
3590 log::info!("completion out of expected range");
3591 continue;
3592 }
3593 };
3594
3595 LineEnding::normalize(&mut new_text);
3596 Some(Completion {
3597 old_range,
3598 new_text,
3599 label: {
3600 match language.as_ref() {
3601 Some(l) => l.label_for_completion(&lsp_completion).await,
3602 None => None,
3603 }
3604 .unwrap_or_else(|| {
3605 CodeLabel::plain(
3606 lsp_completion.label.clone(),
3607 lsp_completion.filter_text.as_deref(),
3608 )
3609 let Range { start, end } = range_for_token
3610 .get_or_insert_with(|| {
3611 let offset = position.to_offset(&snapshot);
3612 let (range, kind) = snapshot.surrounding_word(offset);
3613 if kind == Some(CharKind::Word) {
3614 range
3615 } else {
3616 offset..offset
3617 }
3618 })
3619 .clone();
3620 let text = lsp_completion
3621 .insert_text
3622 .as_ref()
3623 .unwrap_or(&lsp_completion.label)
3624 .clone();
3625 (
3626 snapshot.anchor_before(start)..snapshot.anchor_after(end),
3627 text.clone(),
3628 )
3629 }
3630 Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
3631 log::info!("unsupported insert/replace completion");
3632 continue;
3633 }
3634 };
3635
3636 let partial_completion = PartialCompletion {
3637 old_range,
3638 new_text,
3639 language: language.clone(),
3640 lsp_completion,
3641 };
3642
3643 partial_completions.push(partial_completion);
3644 }
3645 partial_completions
3646 });
3647
3648 let mut result = Vec::new();
3649
3650 for pc in partial_completions.into_iter() {
3651 result.push(async move {
3652 let label = match pc.language.as_ref() {
3653 Some(l) => l.label_for_completion(&pc.lsp_completion).await,
3654 None => None,
3655 }
3656 .unwrap_or_else(|| {
3657 CodeLabel::plain(
3658 pc.lsp_completion.label.clone(),
3659 pc.lsp_completion.filter_text.as_deref(),
3660 )
3661 });
3662
3663 let completion = Completion {
3664 old_range: pc.old_range,
3665 new_text: pc.new_text,
3666 label,
3667 lsp_completion: pc.lsp_completion,
3668 };
3669
3670 completion
3671 });
3672 }
3673
3674 Ok(futures::future::join_all(result).await)
3675 })
3676 } else if let Some(project_id) = self.remote_id() {
3677 let rpc = self.client.clone();
3678 let message = proto::GetCompletions {
3679 project_id,
3680 buffer_id,
3681 position: Some(language::proto::serialize_anchor(&anchor)),
3682 version: serialize_version(&source_buffer.version()),
3683 };
3684 cx.spawn_weak(|_, mut cx| async move {
3685 let response = rpc.request(message).await?;
3686
3687 source_buffer_handle
3688 .update(&mut cx, |buffer, _| {
3689 buffer.wait_for_version(deserialize_version(response.version))
3690 })
3691 .await;
3692
3693 let mut completions = Vec::new();
3694 for completion in response.completions.into_iter() {
3695 completions.push(
3696 language::proto::deserialize_completion(completion, language.clone()).await,
3697 );
3698 }
3699 completions.into_iter().collect()
3700 })
3701 } else {
3702 Task::ready(Ok(Default::default()))
3703 }
3704 }
3705
3706 pub fn apply_additional_edits_for_completion(
3707 &self,
3708 buffer_handle: ModelHandle<Buffer>,
3709 completion: Completion,
3710 push_to_history: bool,
3711 cx: &mut ModelContext<Self>,
3712 ) -> Task<Result<Option<Transaction>>> {
3713 let buffer = buffer_handle.read(cx);
3714 let buffer_id = buffer.remote_id();
3715
3716 if self.is_local() {
3717 let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
3718 {
3719 server.clone()
3720 } else {
3721 return Task::ready(Ok(Default::default()));
3722 };
3723
3724 cx.spawn(|this, mut cx| async move {
3725 let resolved_completion = lang_server
3726 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
3727 .await?;
3728 if let Some(edits) = resolved_completion.additional_text_edits {
3729 let edits = this
3730 .update(&mut cx, |this, cx| {
3731 this.edits_from_lsp(&buffer_handle, edits, None, cx)
3732 })
3733 .await?;
3734 buffer_handle.update(&mut cx, |buffer, cx| {
3735 buffer.finalize_last_transaction();
3736 buffer.start_transaction();
3737 for (range, text) in edits {
3738 buffer.edit([(range, text)], cx);
3739 }
3740 let transaction = if buffer.end_transaction(cx).is_some() {
3741 let transaction = buffer.finalize_last_transaction().unwrap().clone();
3742 if !push_to_history {
3743 buffer.forget_transaction(transaction.id);
3744 }
3745 Some(transaction)
3746 } else {
3747 None
3748 };
3749 Ok(transaction)
3750 })
3751 } else {
3752 Ok(None)
3753 }
3754 })
3755 } else if let Some(project_id) = self.remote_id() {
3756 let client = self.client.clone();
3757 cx.spawn(|_, mut cx| async move {
3758 let response = client
3759 .request(proto::ApplyCompletionAdditionalEdits {
3760 project_id,
3761 buffer_id,
3762 completion: Some(language::proto::serialize_completion(&completion)),
3763 })
3764 .await?;
3765
3766 if let Some(transaction) = response.transaction {
3767 let transaction = language::proto::deserialize_transaction(transaction)?;
3768 buffer_handle
3769 .update(&mut cx, |buffer, _| {
3770 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
3771 })
3772 .await;
3773 if push_to_history {
3774 buffer_handle.update(&mut cx, |buffer, _| {
3775 buffer.push_transaction(transaction.clone(), Instant::now());
3776 });
3777 }
3778 Ok(Some(transaction))
3779 } else {
3780 Ok(None)
3781 }
3782 })
3783 } else {
3784 Task::ready(Err(anyhow!("project does not have a remote id")))
3785 }
3786 }
3787
3788 pub fn code_actions<T: Clone + ToOffset>(
3789 &self,
3790 buffer_handle: &ModelHandle<Buffer>,
3791 range: Range<T>,
3792 cx: &mut ModelContext<Self>,
3793 ) -> Task<Result<Vec<CodeAction>>> {
3794 let buffer_handle = buffer_handle.clone();
3795 let buffer = buffer_handle.read(cx);
3796 let snapshot = buffer.snapshot();
3797 let relevant_diagnostics = snapshot
3798 .diagnostics_in_range::<usize, usize>(range.to_offset(&snapshot), false)
3799 .map(|entry| entry.to_lsp_diagnostic_stub())
3800 .collect();
3801 let buffer_id = buffer.remote_id();
3802 let worktree;
3803 let buffer_abs_path;
3804 if let Some(file) = File::from_dyn(buffer.file()) {
3805 worktree = file.worktree.clone();
3806 buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
3807 } else {
3808 return Task::ready(Ok(Default::default()));
3809 };
3810 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
3811
3812 if worktree.read(cx).as_local().is_some() {
3813 let buffer_abs_path = buffer_abs_path.unwrap();
3814 let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
3815 {
3816 server.clone()
3817 } else {
3818 return Task::ready(Ok(Default::default()));
3819 };
3820
3821 let lsp_range = range_to_lsp(range.to_point_utf16(buffer));
3822 cx.foreground().spawn(async move {
3823 if !lang_server.capabilities().code_action_provider.is_some() {
3824 return Ok(Default::default());
3825 }
3826
3827 Ok(lang_server
3828 .request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
3829 text_document: lsp::TextDocumentIdentifier::new(
3830 lsp::Url::from_file_path(buffer_abs_path).unwrap(),
3831 ),
3832 range: lsp_range,
3833 work_done_progress_params: Default::default(),
3834 partial_result_params: Default::default(),
3835 context: lsp::CodeActionContext {
3836 diagnostics: relevant_diagnostics,
3837 only: Some(vec![
3838 lsp::CodeActionKind::QUICKFIX,
3839 lsp::CodeActionKind::REFACTOR,
3840 lsp::CodeActionKind::REFACTOR_EXTRACT,
3841 lsp::CodeActionKind::SOURCE,
3842 ]),
3843 },
3844 })
3845 .await?
3846 .unwrap_or_default()
3847 .into_iter()
3848 .filter_map(|entry| {
3849 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
3850 Some(CodeAction {
3851 range: range.clone(),
3852 lsp_action,
3853 })
3854 } else {
3855 None
3856 }
3857 })
3858 .collect())
3859 })
3860 } else if let Some(project_id) = self.remote_id() {
3861 let rpc = self.client.clone();
3862 let version = buffer.version();
3863 cx.spawn_weak(|_, mut cx| async move {
3864 let response = rpc
3865 .request(proto::GetCodeActions {
3866 project_id,
3867 buffer_id,
3868 start: Some(language::proto::serialize_anchor(&range.start)),
3869 end: Some(language::proto::serialize_anchor(&range.end)),
3870 version: serialize_version(&version),
3871 })
3872 .await?;
3873
3874 buffer_handle
3875 .update(&mut cx, |buffer, _| {
3876 buffer.wait_for_version(deserialize_version(response.version))
3877 })
3878 .await;
3879
3880 response
3881 .actions
3882 .into_iter()
3883 .map(language::proto::deserialize_code_action)
3884 .collect()
3885 })
3886 } else {
3887 Task::ready(Ok(Default::default()))
3888 }
3889 }
3890
3891 pub fn apply_code_action(
3892 &self,
3893 buffer_handle: ModelHandle<Buffer>,
3894 mut action: CodeAction,
3895 push_to_history: bool,
3896 cx: &mut ModelContext<Self>,
3897 ) -> Task<Result<ProjectTransaction>> {
3898 if self.is_local() {
3899 let buffer = buffer_handle.read(cx);
3900 let (lsp_adapter, lang_server) =
3901 if let Some((adapter, server)) = self.language_server_for_buffer(buffer, cx) {
3902 (adapter.clone(), server.clone())
3903 } else {
3904 return Task::ready(Ok(Default::default()));
3905 };
3906 let range = action.range.to_point_utf16(buffer);
3907
3908 cx.spawn(|this, mut cx| async move {
3909 if let Some(lsp_range) = action
3910 .lsp_action
3911 .data
3912 .as_mut()
3913 .and_then(|d| d.get_mut("codeActionParams"))
3914 .and_then(|d| d.get_mut("range"))
3915 {
3916 *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
3917 action.lsp_action = lang_server
3918 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
3919 .await?;
3920 } else {
3921 let actions = this
3922 .update(&mut cx, |this, cx| {
3923 this.code_actions(&buffer_handle, action.range, cx)
3924 })
3925 .await?;
3926 action.lsp_action = actions
3927 .into_iter()
3928 .find(|a| a.lsp_action.title == action.lsp_action.title)
3929 .ok_or_else(|| anyhow!("code action is outdated"))?
3930 .lsp_action;
3931 }
3932
3933 if let Some(edit) = action.lsp_action.edit {
3934 Self::deserialize_workspace_edit(
3935 this,
3936 edit,
3937 push_to_history,
3938 lsp_adapter.clone(),
3939 lang_server.clone(),
3940 &mut cx,
3941 )
3942 .await
3943 } else if let Some(command) = action.lsp_action.command {
3944 this.update(&mut cx, |this, _| {
3945 this.last_workspace_edits_by_language_server
3946 .remove(&lang_server.server_id());
3947 });
3948 lang_server
3949 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
3950 command: command.command,
3951 arguments: command.arguments.unwrap_or_default(),
3952 ..Default::default()
3953 })
3954 .await?;
3955 Ok(this.update(&mut cx, |this, _| {
3956 this.last_workspace_edits_by_language_server
3957 .remove(&lang_server.server_id())
3958 .unwrap_or_default()
3959 }))
3960 } else {
3961 Ok(ProjectTransaction::default())
3962 }
3963 })
3964 } else if let Some(project_id) = self.remote_id() {
3965 let client = self.client.clone();
3966 let request = proto::ApplyCodeAction {
3967 project_id,
3968 buffer_id: buffer_handle.read(cx).remote_id(),
3969 action: Some(language::proto::serialize_code_action(&action)),
3970 };
3971 cx.spawn(|this, mut cx| async move {
3972 let response = client
3973 .request(request)
3974 .await?
3975 .transaction
3976 .ok_or_else(|| anyhow!("missing transaction"))?;
3977 this.update(&mut cx, |this, cx| {
3978 this.deserialize_project_transaction(response, push_to_history, cx)
3979 })
3980 .await
3981 })
3982 } else {
3983 Task::ready(Err(anyhow!("project does not have a remote id")))
3984 }
3985 }
3986
3987 async fn deserialize_workspace_edit(
3988 this: ModelHandle<Self>,
3989 edit: lsp::WorkspaceEdit,
3990 push_to_history: bool,
3991 lsp_adapter: Arc<LspAdapter>,
3992 language_server: Arc<LanguageServer>,
3993 cx: &mut AsyncAppContext,
3994 ) -> Result<ProjectTransaction> {
3995 let fs = this.read_with(cx, |this, _| this.fs.clone());
3996 let mut operations = Vec::new();
3997 if let Some(document_changes) = edit.document_changes {
3998 match document_changes {
3999 lsp::DocumentChanges::Edits(edits) => {
4000 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
4001 }
4002 lsp::DocumentChanges::Operations(ops) => operations = ops,
4003 }
4004 } else if let Some(changes) = edit.changes {
4005 operations.extend(changes.into_iter().map(|(uri, edits)| {
4006 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
4007 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
4008 uri,
4009 version: None,
4010 },
4011 edits: edits.into_iter().map(lsp::OneOf::Left).collect(),
4012 })
4013 }));
4014 }
4015
4016 let mut project_transaction = ProjectTransaction::default();
4017 for operation in operations {
4018 match operation {
4019 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
4020 let abs_path = op
4021 .uri
4022 .to_file_path()
4023 .map_err(|_| anyhow!("can't convert URI to path"))?;
4024
4025 if let Some(parent_path) = abs_path.parent() {
4026 fs.create_dir(parent_path).await?;
4027 }
4028 if abs_path.ends_with("/") {
4029 fs.create_dir(&abs_path).await?;
4030 } else {
4031 fs.create_file(&abs_path, op.options.map(Into::into).unwrap_or_default())
4032 .await?;
4033 }
4034 }
4035 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
4036 let source_abs_path = op
4037 .old_uri
4038 .to_file_path()
4039 .map_err(|_| anyhow!("can't convert URI to path"))?;
4040 let target_abs_path = op
4041 .new_uri
4042 .to_file_path()
4043 .map_err(|_| anyhow!("can't convert URI to path"))?;
4044 fs.rename(
4045 &source_abs_path,
4046 &target_abs_path,
4047 op.options.map(Into::into).unwrap_or_default(),
4048 )
4049 .await?;
4050 }
4051 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
4052 let abs_path = op
4053 .uri
4054 .to_file_path()
4055 .map_err(|_| anyhow!("can't convert URI to path"))?;
4056 let options = op.options.map(Into::into).unwrap_or_default();
4057 if abs_path.ends_with("/") {
4058 fs.remove_dir(&abs_path, options).await?;
4059 } else {
4060 fs.remove_file(&abs_path, options).await?;
4061 }
4062 }
4063 lsp::DocumentChangeOperation::Edit(op) => {
4064 let buffer_to_edit = this
4065 .update(cx, |this, cx| {
4066 this.open_local_buffer_via_lsp(
4067 op.text_document.uri,
4068 language_server.server_id(),
4069 lsp_adapter.name(),
4070 cx,
4071 )
4072 })
4073 .await?;
4074
4075 let edits = this
4076 .update(cx, |this, cx| {
4077 let edits = op.edits.into_iter().map(|edit| match edit {
4078 lsp::OneOf::Left(edit) => edit,
4079 lsp::OneOf::Right(edit) => edit.text_edit,
4080 });
4081 this.edits_from_lsp(
4082 &buffer_to_edit,
4083 edits,
4084 op.text_document.version,
4085 cx,
4086 )
4087 })
4088 .await?;
4089
4090 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
4091 buffer.finalize_last_transaction();
4092 buffer.start_transaction();
4093 for (range, text) in edits {
4094 buffer.edit([(range, text)], cx);
4095 }
4096 let transaction = if buffer.end_transaction(cx).is_some() {
4097 let transaction = buffer.finalize_last_transaction().unwrap().clone();
4098 if !push_to_history {
4099 buffer.forget_transaction(transaction.id);
4100 }
4101 Some(transaction)
4102 } else {
4103 None
4104 };
4105
4106 transaction
4107 });
4108 if let Some(transaction) = transaction {
4109 project_transaction.0.insert(buffer_to_edit, transaction);
4110 }
4111 }
4112 }
4113 }
4114
4115 Ok(project_transaction)
4116 }
4117
4118 pub fn prepare_rename<T: ToPointUtf16>(
4119 &self,
4120 buffer: ModelHandle<Buffer>,
4121 position: T,
4122 cx: &mut ModelContext<Self>,
4123 ) -> Task<Result<Option<Range<Anchor>>>> {
4124 let position = position.to_point_utf16(buffer.read(cx));
4125 self.request_lsp(buffer, PrepareRename { position }, cx)
4126 }
4127
4128 pub fn perform_rename<T: ToPointUtf16>(
4129 &self,
4130 buffer: ModelHandle<Buffer>,
4131 position: T,
4132 new_name: String,
4133 push_to_history: bool,
4134 cx: &mut ModelContext<Self>,
4135 ) -> Task<Result<ProjectTransaction>> {
4136 let position = position.to_point_utf16(buffer.read(cx));
4137 self.request_lsp(
4138 buffer,
4139 PerformRename {
4140 position,
4141 new_name,
4142 push_to_history,
4143 },
4144 cx,
4145 )
4146 }
4147
4148 pub fn search(
4149 &self,
4150 query: SearchQuery,
4151 cx: &mut ModelContext<Self>,
4152 ) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<Range<Anchor>>>>> {
4153 if self.is_local() {
4154 let snapshots = self
4155 .visible_worktrees(cx)
4156 .filter_map(|tree| {
4157 let tree = tree.read(cx).as_local()?;
4158 Some(tree.snapshot())
4159 })
4160 .collect::<Vec<_>>();
4161
4162 let background = cx.background().clone();
4163 let path_count: usize = snapshots.iter().map(|s| s.visible_file_count()).sum();
4164 if path_count == 0 {
4165 return Task::ready(Ok(Default::default()));
4166 }
4167 let workers = background.num_cpus().min(path_count);
4168 let (matching_paths_tx, mut matching_paths_rx) = smol::channel::bounded(1024);
4169 cx.background()
4170 .spawn({
4171 let fs = self.fs.clone();
4172 let background = cx.background().clone();
4173 let query = query.clone();
4174 async move {
4175 let fs = &fs;
4176 let query = &query;
4177 let matching_paths_tx = &matching_paths_tx;
4178 let paths_per_worker = (path_count + workers - 1) / workers;
4179 let snapshots = &snapshots;
4180 background
4181 .scoped(|scope| {
4182 for worker_ix in 0..workers {
4183 let worker_start_ix = worker_ix * paths_per_worker;
4184 let worker_end_ix = worker_start_ix + paths_per_worker;
4185 scope.spawn(async move {
4186 let mut snapshot_start_ix = 0;
4187 let mut abs_path = PathBuf::new();
4188 for snapshot in snapshots {
4189 let snapshot_end_ix =
4190 snapshot_start_ix + snapshot.visible_file_count();
4191 if worker_end_ix <= snapshot_start_ix {
4192 break;
4193 } else if worker_start_ix > snapshot_end_ix {
4194 snapshot_start_ix = snapshot_end_ix;
4195 continue;
4196 } else {
4197 let start_in_snapshot = worker_start_ix
4198 .saturating_sub(snapshot_start_ix);
4199 let end_in_snapshot =
4200 cmp::min(worker_end_ix, snapshot_end_ix)
4201 - snapshot_start_ix;
4202
4203 for entry in snapshot
4204 .files(false, start_in_snapshot)
4205 .take(end_in_snapshot - start_in_snapshot)
4206 {
4207 if matching_paths_tx.is_closed() {
4208 break;
4209 }
4210
4211 abs_path.clear();
4212 abs_path.push(&snapshot.abs_path());
4213 abs_path.push(&entry.path);
4214 let matches = if let Some(file) =
4215 fs.open_sync(&abs_path).await.log_err()
4216 {
4217 query.detect(file).unwrap_or(false)
4218 } else {
4219 false
4220 };
4221
4222 if matches {
4223 let project_path =
4224 (snapshot.id(), entry.path.clone());
4225 if matching_paths_tx
4226 .send(project_path)
4227 .await
4228 .is_err()
4229 {
4230 break;
4231 }
4232 }
4233 }
4234
4235 snapshot_start_ix = snapshot_end_ix;
4236 }
4237 }
4238 });
4239 }
4240 })
4241 .await;
4242 }
4243 })
4244 .detach();
4245
4246 let (buffers_tx, buffers_rx) = smol::channel::bounded(1024);
4247 let open_buffers = self
4248 .opened_buffers
4249 .values()
4250 .filter_map(|b| b.upgrade(cx))
4251 .collect::<HashSet<_>>();
4252 cx.spawn(|this, cx| async move {
4253 for buffer in &open_buffers {
4254 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
4255 buffers_tx.send((buffer.clone(), snapshot)).await?;
4256 }
4257
4258 let open_buffers = Rc::new(RefCell::new(open_buffers));
4259 while let Some(project_path) = matching_paths_rx.next().await {
4260 if buffers_tx.is_closed() {
4261 break;
4262 }
4263
4264 let this = this.clone();
4265 let open_buffers = open_buffers.clone();
4266 let buffers_tx = buffers_tx.clone();
4267 cx.spawn(|mut cx| async move {
4268 if let Some(buffer) = this
4269 .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
4270 .await
4271 .log_err()
4272 {
4273 if open_buffers.borrow_mut().insert(buffer.clone()) {
4274 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
4275 buffers_tx.send((buffer, snapshot)).await?;
4276 }
4277 }
4278
4279 Ok::<_, anyhow::Error>(())
4280 })
4281 .detach();
4282 }
4283
4284 Ok::<_, anyhow::Error>(())
4285 })
4286 .detach_and_log_err(cx);
4287
4288 let background = cx.background().clone();
4289 cx.background().spawn(async move {
4290 let query = &query;
4291 let mut matched_buffers = Vec::new();
4292 for _ in 0..workers {
4293 matched_buffers.push(HashMap::default());
4294 }
4295 background
4296 .scoped(|scope| {
4297 for worker_matched_buffers in matched_buffers.iter_mut() {
4298 let mut buffers_rx = buffers_rx.clone();
4299 scope.spawn(async move {
4300 while let Some((buffer, snapshot)) = buffers_rx.next().await {
4301 let buffer_matches = query
4302 .search(snapshot.as_rope())
4303 .await
4304 .iter()
4305 .map(|range| {
4306 snapshot.anchor_before(range.start)
4307 ..snapshot.anchor_after(range.end)
4308 })
4309 .collect::<Vec<_>>();
4310 if !buffer_matches.is_empty() {
4311 worker_matched_buffers
4312 .insert(buffer.clone(), buffer_matches);
4313 }
4314 }
4315 });
4316 }
4317 })
4318 .await;
4319 Ok(matched_buffers.into_iter().flatten().collect())
4320 })
4321 } else if let Some(project_id) = self.remote_id() {
4322 let request = self.client.request(query.to_proto(project_id));
4323 cx.spawn(|this, mut cx| async move {
4324 let response = request.await?;
4325 let mut result = HashMap::default();
4326 for location in response.locations {
4327 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
4328 let target_buffer = this
4329 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
4330 .await?;
4331 let start = location
4332 .start
4333 .and_then(deserialize_anchor)
4334 .ok_or_else(|| anyhow!("missing target start"))?;
4335 let end = location
4336 .end
4337 .and_then(deserialize_anchor)
4338 .ok_or_else(|| anyhow!("missing target end"))?;
4339 result
4340 .entry(target_buffer)
4341 .or_insert(Vec::new())
4342 .push(start..end)
4343 }
4344 Ok(result)
4345 })
4346 } else {
4347 Task::ready(Ok(Default::default()))
4348 }
4349 }
4350
4351 fn request_lsp<R: LspCommand>(
4352 &self,
4353 buffer_handle: ModelHandle<Buffer>,
4354 request: R,
4355 cx: &mut ModelContext<Self>,
4356 ) -> Task<Result<R::Response>>
4357 where
4358 <R::LspRequest as lsp::request::Request>::Result: Send,
4359 {
4360 let buffer = buffer_handle.read(cx);
4361 if self.is_local() {
4362 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
4363 if let Some((file, language_server)) = file.zip(
4364 self.language_server_for_buffer(buffer, cx)
4365 .map(|(_, server)| server.clone()),
4366 ) {
4367 let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
4368 return cx.spawn(|this, cx| async move {
4369 if !request.check_capabilities(&language_server.capabilities()) {
4370 return Ok(Default::default());
4371 }
4372
4373 let response = language_server
4374 .request::<R::LspRequest>(lsp_params)
4375 .await
4376 .context("lsp request failed")?;
4377 request
4378 .response_from_lsp(response, this, buffer_handle, cx)
4379 .await
4380 });
4381 }
4382 } else if let Some(project_id) = self.remote_id() {
4383 let rpc = self.client.clone();
4384 let message = request.to_proto(project_id, buffer);
4385 return cx.spawn(|this, cx| async move {
4386 let response = rpc.request(message).await?;
4387 request
4388 .response_from_proto(response, this, buffer_handle, cx)
4389 .await
4390 });
4391 }
4392 Task::ready(Ok(Default::default()))
4393 }
4394
4395 pub fn find_or_create_local_worktree(
4396 &mut self,
4397 abs_path: impl AsRef<Path>,
4398 visible: bool,
4399 cx: &mut ModelContext<Self>,
4400 ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
4401 let abs_path = abs_path.as_ref();
4402 if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
4403 Task::ready(Ok((tree.clone(), relative_path.into())))
4404 } else {
4405 let worktree = self.create_local_worktree(abs_path, visible, cx);
4406 cx.foreground()
4407 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
4408 }
4409 }
4410
4411 pub fn find_local_worktree(
4412 &self,
4413 abs_path: &Path,
4414 cx: &AppContext,
4415 ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
4416 for tree in &self.worktrees {
4417 if let Some(tree) = tree.upgrade(cx) {
4418 if let Some(relative_path) = tree
4419 .read(cx)
4420 .as_local()
4421 .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
4422 {
4423 return Some((tree.clone(), relative_path.into()));
4424 }
4425 }
4426 }
4427 None
4428 }
4429
4430 pub fn is_shared(&self) -> bool {
4431 match &self.client_state {
4432 ProjectClientState::Local { is_shared, .. } => *is_shared,
4433 ProjectClientState::Remote { .. } => false,
4434 }
4435 }
4436
4437 fn create_local_worktree(
4438 &mut self,
4439 abs_path: impl AsRef<Path>,
4440 visible: bool,
4441 cx: &mut ModelContext<Self>,
4442 ) -> Task<Result<ModelHandle<Worktree>>> {
4443 let fs = self.fs.clone();
4444 let client = self.client.clone();
4445 let next_entry_id = self.next_entry_id.clone();
4446 let path: Arc<Path> = abs_path.as_ref().into();
4447 let task = self
4448 .loading_local_worktrees
4449 .entry(path.clone())
4450 .or_insert_with(|| {
4451 cx.spawn(|project, mut cx| {
4452 async move {
4453 let worktree = Worktree::local(
4454 client.clone(),
4455 path.clone(),
4456 visible,
4457 fs,
4458 next_entry_id,
4459 &mut cx,
4460 )
4461 .await;
4462 project.update(&mut cx, |project, _| {
4463 project.loading_local_worktrees.remove(&path);
4464 });
4465 let worktree = worktree?;
4466
4467 let project_id = project.update(&mut cx, |project, cx| {
4468 project.add_worktree(&worktree, cx);
4469 project.shared_remote_id()
4470 });
4471
4472 if let Some(project_id) = project_id {
4473 worktree
4474 .update(&mut cx, |worktree, cx| {
4475 worktree.as_local_mut().unwrap().share(project_id, cx)
4476 })
4477 .await
4478 .log_err();
4479 }
4480
4481 Ok(worktree)
4482 }
4483 .map_err(|err| Arc::new(err))
4484 })
4485 .shared()
4486 })
4487 .clone();
4488 cx.foreground().spawn(async move {
4489 match task.await {
4490 Ok(worktree) => Ok(worktree),
4491 Err(err) => Err(anyhow!("{}", err)),
4492 }
4493 })
4494 }
4495
4496 pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
4497 self.worktrees.retain(|worktree| {
4498 if let Some(worktree) = worktree.upgrade(cx) {
4499 let id = worktree.read(cx).id();
4500 if id == id_to_remove {
4501 cx.emit(Event::WorktreeRemoved(id));
4502 false
4503 } else {
4504 true
4505 }
4506 } else {
4507 false
4508 }
4509 });
4510 self.metadata_changed(true, cx);
4511 cx.notify();
4512 }
4513
4514 fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
4515 cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
4516 if worktree.read(cx).is_local() {
4517 cx.subscribe(&worktree, |this, worktree, _, cx| {
4518 this.update_local_worktree_buffers(worktree, cx);
4519 })
4520 .detach();
4521 }
4522
4523 let push_strong_handle = {
4524 let worktree = worktree.read(cx);
4525 self.is_shared() || worktree.is_visible() || worktree.is_remote()
4526 };
4527 if push_strong_handle {
4528 self.worktrees
4529 .push(WorktreeHandle::Strong(worktree.clone()));
4530 } else {
4531 self.worktrees
4532 .push(WorktreeHandle::Weak(worktree.downgrade()));
4533 }
4534
4535 self.metadata_changed(true, cx);
4536 cx.observe_release(&worktree, |this, worktree, cx| {
4537 this.remove_worktree(worktree.id(), cx);
4538 cx.notify();
4539 })
4540 .detach();
4541
4542 cx.emit(Event::WorktreeAdded);
4543 cx.notify();
4544 }
4545
4546 fn update_local_worktree_buffers(
4547 &mut self,
4548 worktree_handle: ModelHandle<Worktree>,
4549 cx: &mut ModelContext<Self>,
4550 ) {
4551 let snapshot = worktree_handle.read(cx).snapshot();
4552 let mut buffers_to_delete = Vec::new();
4553 let mut renamed_buffers = Vec::new();
4554 for (buffer_id, buffer) in &self.opened_buffers {
4555 if let Some(buffer) = buffer.upgrade(cx) {
4556 buffer.update(cx, |buffer, cx| {
4557 if let Some(old_file) = File::from_dyn(buffer.file()) {
4558 if old_file.worktree != worktree_handle {
4559 return;
4560 }
4561
4562 let new_file = if let Some(entry) = old_file
4563 .entry_id
4564 .and_then(|entry_id| snapshot.entry_for_id(entry_id))
4565 {
4566 File {
4567 is_local: true,
4568 entry_id: Some(entry.id),
4569 mtime: entry.mtime,
4570 path: entry.path.clone(),
4571 worktree: worktree_handle.clone(),
4572 }
4573 } else if let Some(entry) =
4574 snapshot.entry_for_path(old_file.path().as_ref())
4575 {
4576 File {
4577 is_local: true,
4578 entry_id: Some(entry.id),
4579 mtime: entry.mtime,
4580 path: entry.path.clone(),
4581 worktree: worktree_handle.clone(),
4582 }
4583 } else {
4584 File {
4585 is_local: true,
4586 entry_id: None,
4587 path: old_file.path().clone(),
4588 mtime: old_file.mtime(),
4589 worktree: worktree_handle.clone(),
4590 }
4591 };
4592
4593 let old_path = old_file.abs_path(cx);
4594 if new_file.abs_path(cx) != old_path {
4595 renamed_buffers.push((cx.handle(), old_path));
4596 }
4597
4598 if let Some(project_id) = self.shared_remote_id() {
4599 self.client
4600 .send(proto::UpdateBufferFile {
4601 project_id,
4602 buffer_id: *buffer_id as u64,
4603 file: Some(new_file.to_proto()),
4604 })
4605 .log_err();
4606 }
4607 buffer.file_updated(Arc::new(new_file), cx).detach();
4608 }
4609 });
4610 } else {
4611 buffers_to_delete.push(*buffer_id);
4612 }
4613 }
4614
4615 for buffer_id in buffers_to_delete {
4616 self.opened_buffers.remove(&buffer_id);
4617 }
4618
4619 for (buffer, old_path) in renamed_buffers {
4620 self.unregister_buffer_from_language_server(&buffer, old_path, cx);
4621 self.assign_language_to_buffer(&buffer, cx);
4622 self.register_buffer_with_language_server(&buffer, cx);
4623 }
4624 }
4625
4626 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
4627 let new_active_entry = entry.and_then(|project_path| {
4628 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
4629 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
4630 Some(entry.id)
4631 });
4632 if new_active_entry != self.active_entry {
4633 self.active_entry = new_active_entry;
4634 cx.emit(Event::ActiveEntryChanged(new_active_entry));
4635 }
4636 }
4637
4638 pub fn language_servers_running_disk_based_diagnostics<'a>(
4639 &'a self,
4640 ) -> impl 'a + Iterator<Item = usize> {
4641 self.language_server_statuses
4642 .iter()
4643 .filter_map(|(id, status)| {
4644 if status.has_pending_diagnostic_updates {
4645 Some(*id)
4646 } else {
4647 None
4648 }
4649 })
4650 }
4651
4652 pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
4653 let mut summary = DiagnosticSummary::default();
4654 for (_, path_summary) in self.diagnostic_summaries(cx) {
4655 summary.error_count += path_summary.error_count;
4656 summary.warning_count += path_summary.warning_count;
4657 }
4658 summary
4659 }
4660
4661 pub fn diagnostic_summaries<'a>(
4662 &'a self,
4663 cx: &'a AppContext,
4664 ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
4665 self.visible_worktrees(cx).flat_map(move |worktree| {
4666 let worktree = worktree.read(cx);
4667 let worktree_id = worktree.id();
4668 worktree
4669 .diagnostic_summaries()
4670 .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
4671 })
4672 }
4673
4674 pub fn disk_based_diagnostics_started(
4675 &mut self,
4676 language_server_id: usize,
4677 cx: &mut ModelContext<Self>,
4678 ) {
4679 cx.emit(Event::DiskBasedDiagnosticsStarted { language_server_id });
4680 }
4681
4682 pub fn disk_based_diagnostics_finished(
4683 &mut self,
4684 language_server_id: usize,
4685 cx: &mut ModelContext<Self>,
4686 ) {
4687 cx.emit(Event::DiskBasedDiagnosticsFinished { language_server_id });
4688 }
4689
4690 pub fn active_entry(&self) -> Option<ProjectEntryId> {
4691 self.active_entry
4692 }
4693
4694 pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<ProjectEntryId> {
4695 self.worktree_for_id(path.worktree_id, cx)?
4696 .read(cx)
4697 .entry_for_path(&path.path)
4698 .map(|entry| entry.id)
4699 }
4700
4701 pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
4702 let worktree = self.worktree_for_entry(entry_id, cx)?;
4703 let worktree = worktree.read(cx);
4704 let worktree_id = worktree.id();
4705 let path = worktree.entry_for_id(entry_id)?.path.clone();
4706 Some(ProjectPath { worktree_id, path })
4707 }
4708
4709 // RPC message handlers
4710
4711 async fn handle_request_join_project(
4712 this: ModelHandle<Self>,
4713 message: TypedEnvelope<proto::RequestJoinProject>,
4714 _: Arc<Client>,
4715 mut cx: AsyncAppContext,
4716 ) -> Result<()> {
4717 let user_id = message.payload.requester_id;
4718 if this.read_with(&cx, |project, _| {
4719 project.collaborators.values().any(|c| c.user.id == user_id)
4720 }) {
4721 this.update(&mut cx, |this, cx| {
4722 this.respond_to_join_request(user_id, true, cx)
4723 });
4724 } else {
4725 let user_store = this.read_with(&cx, |this, _| this.user_store.clone());
4726 let user = user_store
4727 .update(&mut cx, |store, cx| store.fetch_user(user_id, cx))
4728 .await?;
4729 this.update(&mut cx, |_, cx| cx.emit(Event::ContactRequestedJoin(user)));
4730 }
4731 Ok(())
4732 }
4733
4734 async fn handle_unregister_project(
4735 this: ModelHandle<Self>,
4736 _: TypedEnvelope<proto::UnregisterProject>,
4737 _: Arc<Client>,
4738 mut cx: AsyncAppContext,
4739 ) -> Result<()> {
4740 this.update(&mut cx, |this, cx| this.removed_from_project(cx));
4741 Ok(())
4742 }
4743
4744 async fn handle_project_unshared(
4745 this: ModelHandle<Self>,
4746 _: TypedEnvelope<proto::ProjectUnshared>,
4747 _: Arc<Client>,
4748 mut cx: AsyncAppContext,
4749 ) -> Result<()> {
4750 this.update(&mut cx, |this, cx| this.unshared(cx));
4751 Ok(())
4752 }
4753
4754 async fn handle_add_collaborator(
4755 this: ModelHandle<Self>,
4756 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
4757 _: Arc<Client>,
4758 mut cx: AsyncAppContext,
4759 ) -> Result<()> {
4760 let user_store = this.read_with(&cx, |this, _| this.user_store.clone());
4761 let collaborator = envelope
4762 .payload
4763 .collaborator
4764 .take()
4765 .ok_or_else(|| anyhow!("empty collaborator"))?;
4766
4767 let collaborator = Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
4768 this.update(&mut cx, |this, cx| {
4769 this.collaborators
4770 .insert(collaborator.peer_id, collaborator);
4771 cx.notify();
4772 });
4773
4774 Ok(())
4775 }
4776
4777 async fn handle_remove_collaborator(
4778 this: ModelHandle<Self>,
4779 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
4780 _: Arc<Client>,
4781 mut cx: AsyncAppContext,
4782 ) -> Result<()> {
4783 this.update(&mut cx, |this, cx| {
4784 let peer_id = PeerId(envelope.payload.peer_id);
4785 let replica_id = this
4786 .collaborators
4787 .remove(&peer_id)
4788 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
4789 .replica_id;
4790 for (_, buffer) in &this.opened_buffers {
4791 if let Some(buffer) = buffer.upgrade(cx) {
4792 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
4793 }
4794 }
4795
4796 cx.emit(Event::CollaboratorLeft(peer_id));
4797 cx.notify();
4798 Ok(())
4799 })
4800 }
4801
4802 async fn handle_join_project_request_cancelled(
4803 this: ModelHandle<Self>,
4804 envelope: TypedEnvelope<proto::JoinProjectRequestCancelled>,
4805 _: Arc<Client>,
4806 mut cx: AsyncAppContext,
4807 ) -> Result<()> {
4808 let user = this
4809 .update(&mut cx, |this, cx| {
4810 this.user_store.update(cx, |user_store, cx| {
4811 user_store.fetch_user(envelope.payload.requester_id, cx)
4812 })
4813 })
4814 .await?;
4815
4816 this.update(&mut cx, |_, cx| {
4817 cx.emit(Event::ContactCancelledJoinRequest(user));
4818 });
4819
4820 Ok(())
4821 }
4822
4823 async fn handle_update_project(
4824 this: ModelHandle<Self>,
4825 envelope: TypedEnvelope<proto::UpdateProject>,
4826 client: Arc<Client>,
4827 mut cx: AsyncAppContext,
4828 ) -> Result<()> {
4829 this.update(&mut cx, |this, cx| {
4830 let replica_id = this.replica_id();
4831 let remote_id = this.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
4832
4833 let mut old_worktrees_by_id = this
4834 .worktrees
4835 .drain(..)
4836 .filter_map(|worktree| {
4837 let worktree = worktree.upgrade(cx)?;
4838 Some((worktree.read(cx).id(), worktree))
4839 })
4840 .collect::<HashMap<_, _>>();
4841
4842 for worktree in envelope.payload.worktrees {
4843 if let Some(old_worktree) =
4844 old_worktrees_by_id.remove(&WorktreeId::from_proto(worktree.id))
4845 {
4846 this.worktrees.push(WorktreeHandle::Strong(old_worktree));
4847 } else {
4848 let worktree =
4849 Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx);
4850 this.add_worktree(&worktree, cx);
4851 }
4852 }
4853
4854 this.metadata_changed(true, cx);
4855 for (id, _) in old_worktrees_by_id {
4856 cx.emit(Event::WorktreeRemoved(id));
4857 }
4858
4859 Ok(())
4860 })
4861 }
4862
4863 async fn handle_update_worktree(
4864 this: ModelHandle<Self>,
4865 envelope: TypedEnvelope<proto::UpdateWorktree>,
4866 _: Arc<Client>,
4867 mut cx: AsyncAppContext,
4868 ) -> Result<()> {
4869 this.update(&mut cx, |this, cx| {
4870 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4871 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
4872 worktree.update(cx, |worktree, _| {
4873 let worktree = worktree.as_remote_mut().unwrap();
4874 worktree.update_from_remote(envelope.payload);
4875 });
4876 }
4877 Ok(())
4878 })
4879 }
4880
4881 async fn handle_create_project_entry(
4882 this: ModelHandle<Self>,
4883 envelope: TypedEnvelope<proto::CreateProjectEntry>,
4884 _: Arc<Client>,
4885 mut cx: AsyncAppContext,
4886 ) -> Result<proto::ProjectEntryResponse> {
4887 let worktree = this.update(&mut cx, |this, cx| {
4888 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4889 this.worktree_for_id(worktree_id, cx)
4890 .ok_or_else(|| anyhow!("worktree not found"))
4891 })?;
4892 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
4893 let entry = worktree
4894 .update(&mut cx, |worktree, cx| {
4895 let worktree = worktree.as_local_mut().unwrap();
4896 let path = PathBuf::from(OsString::from_vec(envelope.payload.path));
4897 worktree.create_entry(path, envelope.payload.is_directory, cx)
4898 })
4899 .await?;
4900 Ok(proto::ProjectEntryResponse {
4901 entry: Some((&entry).into()),
4902 worktree_scan_id: worktree_scan_id as u64,
4903 })
4904 }
4905
4906 async fn handle_rename_project_entry(
4907 this: ModelHandle<Self>,
4908 envelope: TypedEnvelope<proto::RenameProjectEntry>,
4909 _: Arc<Client>,
4910 mut cx: AsyncAppContext,
4911 ) -> Result<proto::ProjectEntryResponse> {
4912 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
4913 let worktree = this.read_with(&cx, |this, cx| {
4914 this.worktree_for_entry(entry_id, cx)
4915 .ok_or_else(|| anyhow!("worktree not found"))
4916 })?;
4917 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
4918 let entry = worktree
4919 .update(&mut cx, |worktree, cx| {
4920 let new_path = PathBuf::from(OsString::from_vec(envelope.payload.new_path));
4921 worktree
4922 .as_local_mut()
4923 .unwrap()
4924 .rename_entry(entry_id, new_path, cx)
4925 .ok_or_else(|| anyhow!("invalid entry"))
4926 })?
4927 .await?;
4928 Ok(proto::ProjectEntryResponse {
4929 entry: Some((&entry).into()),
4930 worktree_scan_id: worktree_scan_id as u64,
4931 })
4932 }
4933
4934 async fn handle_copy_project_entry(
4935 this: ModelHandle<Self>,
4936 envelope: TypedEnvelope<proto::CopyProjectEntry>,
4937 _: Arc<Client>,
4938 mut cx: AsyncAppContext,
4939 ) -> Result<proto::ProjectEntryResponse> {
4940 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
4941 let worktree = this.read_with(&cx, |this, cx| {
4942 this.worktree_for_entry(entry_id, cx)
4943 .ok_or_else(|| anyhow!("worktree not found"))
4944 })?;
4945 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
4946 let entry = worktree
4947 .update(&mut cx, |worktree, cx| {
4948 let new_path = PathBuf::from(OsString::from_vec(envelope.payload.new_path));
4949 worktree
4950 .as_local_mut()
4951 .unwrap()
4952 .copy_entry(entry_id, new_path, cx)
4953 .ok_or_else(|| anyhow!("invalid entry"))
4954 })?
4955 .await?;
4956 Ok(proto::ProjectEntryResponse {
4957 entry: Some((&entry).into()),
4958 worktree_scan_id: worktree_scan_id as u64,
4959 })
4960 }
4961
4962 async fn handle_delete_project_entry(
4963 this: ModelHandle<Self>,
4964 envelope: TypedEnvelope<proto::DeleteProjectEntry>,
4965 _: Arc<Client>,
4966 mut cx: AsyncAppContext,
4967 ) -> Result<proto::ProjectEntryResponse> {
4968 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
4969 let worktree = this.read_with(&cx, |this, cx| {
4970 this.worktree_for_entry(entry_id, cx)
4971 .ok_or_else(|| anyhow!("worktree not found"))
4972 })?;
4973 let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
4974 worktree
4975 .update(&mut cx, |worktree, cx| {
4976 worktree
4977 .as_local_mut()
4978 .unwrap()
4979 .delete_entry(entry_id, cx)
4980 .ok_or_else(|| anyhow!("invalid entry"))
4981 })?
4982 .await?;
4983 Ok(proto::ProjectEntryResponse {
4984 entry: None,
4985 worktree_scan_id: worktree_scan_id as u64,
4986 })
4987 }
4988
4989 async fn handle_update_diagnostic_summary(
4990 this: ModelHandle<Self>,
4991 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
4992 _: Arc<Client>,
4993 mut cx: AsyncAppContext,
4994 ) -> Result<()> {
4995 this.update(&mut cx, |this, cx| {
4996 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4997 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
4998 if let Some(summary) = envelope.payload.summary {
4999 let project_path = ProjectPath {
5000 worktree_id,
5001 path: Path::new(&summary.path).into(),
5002 };
5003 worktree.update(cx, |worktree, _| {
5004 worktree
5005 .as_remote_mut()
5006 .unwrap()
5007 .update_diagnostic_summary(project_path.path.clone(), &summary);
5008 });
5009 cx.emit(Event::DiagnosticsUpdated {
5010 language_server_id: summary.language_server_id as usize,
5011 path: project_path,
5012 });
5013 }
5014 }
5015 Ok(())
5016 })
5017 }
5018
5019 async fn handle_start_language_server(
5020 this: ModelHandle<Self>,
5021 envelope: TypedEnvelope<proto::StartLanguageServer>,
5022 _: Arc<Client>,
5023 mut cx: AsyncAppContext,
5024 ) -> Result<()> {
5025 let server = envelope
5026 .payload
5027 .server
5028 .ok_or_else(|| anyhow!("invalid server"))?;
5029 this.update(&mut cx, |this, cx| {
5030 this.language_server_statuses.insert(
5031 server.id as usize,
5032 LanguageServerStatus {
5033 name: server.name,
5034 pending_work: Default::default(),
5035 has_pending_diagnostic_updates: false,
5036 progress_tokens: Default::default(),
5037 },
5038 );
5039 cx.notify();
5040 });
5041 Ok(())
5042 }
5043
5044 async fn handle_update_language_server(
5045 this: ModelHandle<Self>,
5046 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
5047 _: Arc<Client>,
5048 mut cx: AsyncAppContext,
5049 ) -> Result<()> {
5050 let language_server_id = envelope.payload.language_server_id as usize;
5051 match envelope
5052 .payload
5053 .variant
5054 .ok_or_else(|| anyhow!("invalid variant"))?
5055 {
5056 proto::update_language_server::Variant::WorkStart(payload) => {
5057 this.update(&mut cx, |this, cx| {
5058 this.on_lsp_work_start(
5059 language_server_id,
5060 payload.token,
5061 LanguageServerProgress {
5062 message: payload.message,
5063 percentage: payload.percentage.map(|p| p as usize),
5064 last_update_at: Instant::now(),
5065 },
5066 cx,
5067 );
5068 })
5069 }
5070 proto::update_language_server::Variant::WorkProgress(payload) => {
5071 this.update(&mut cx, |this, cx| {
5072 this.on_lsp_work_progress(
5073 language_server_id,
5074 payload.token,
5075 LanguageServerProgress {
5076 message: payload.message,
5077 percentage: payload.percentage.map(|p| p as usize),
5078 last_update_at: Instant::now(),
5079 },
5080 cx,
5081 );
5082 })
5083 }
5084 proto::update_language_server::Variant::WorkEnd(payload) => {
5085 this.update(&mut cx, |this, cx| {
5086 this.on_lsp_work_end(language_server_id, payload.token, cx);
5087 })
5088 }
5089 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
5090 this.update(&mut cx, |this, cx| {
5091 this.disk_based_diagnostics_started(language_server_id, cx);
5092 })
5093 }
5094 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
5095 this.update(&mut cx, |this, cx| {
5096 this.disk_based_diagnostics_finished(language_server_id, cx)
5097 });
5098 }
5099 }
5100
5101 Ok(())
5102 }
5103
5104 async fn handle_update_buffer(
5105 this: ModelHandle<Self>,
5106 envelope: TypedEnvelope<proto::UpdateBuffer>,
5107 _: Arc<Client>,
5108 mut cx: AsyncAppContext,
5109 ) -> Result<()> {
5110 this.update(&mut cx, |this, cx| {
5111 let payload = envelope.payload.clone();
5112 let buffer_id = payload.buffer_id;
5113 let ops = payload
5114 .operations
5115 .into_iter()
5116 .map(|op| language::proto::deserialize_operation(op))
5117 .collect::<Result<Vec<_>, _>>()?;
5118 let is_remote = this.is_remote();
5119 match this.opened_buffers.entry(buffer_id) {
5120 hash_map::Entry::Occupied(mut e) => match e.get_mut() {
5121 OpenBuffer::Strong(buffer) => {
5122 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
5123 }
5124 OpenBuffer::Loading(operations) => operations.extend_from_slice(&ops),
5125 OpenBuffer::Weak(_) => {}
5126 },
5127 hash_map::Entry::Vacant(e) => {
5128 assert!(
5129 is_remote,
5130 "received buffer update from {:?}",
5131 envelope.original_sender_id
5132 );
5133 e.insert(OpenBuffer::Loading(ops));
5134 }
5135 }
5136 Ok(())
5137 })
5138 }
5139
5140 async fn handle_update_buffer_file(
5141 this: ModelHandle<Self>,
5142 envelope: TypedEnvelope<proto::UpdateBufferFile>,
5143 _: Arc<Client>,
5144 mut cx: AsyncAppContext,
5145 ) -> Result<()> {
5146 this.update(&mut cx, |this, cx| {
5147 let payload = envelope.payload.clone();
5148 let buffer_id = payload.buffer_id;
5149 let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
5150 let worktree = this
5151 .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
5152 .ok_or_else(|| anyhow!("no such worktree"))?;
5153 let file = File::from_proto(file, worktree.clone(), cx)?;
5154 let buffer = this
5155 .opened_buffers
5156 .get_mut(&buffer_id)
5157 .and_then(|b| b.upgrade(cx))
5158 .ok_or_else(|| anyhow!("no such buffer"))?;
5159 buffer.update(cx, |buffer, cx| {
5160 buffer.file_updated(Arc::new(file), cx).detach();
5161 });
5162 Ok(())
5163 })
5164 }
5165
5166 async fn handle_save_buffer(
5167 this: ModelHandle<Self>,
5168 envelope: TypedEnvelope<proto::SaveBuffer>,
5169 _: Arc<Client>,
5170 mut cx: AsyncAppContext,
5171 ) -> Result<proto::BufferSaved> {
5172 let buffer_id = envelope.payload.buffer_id;
5173 let requested_version = deserialize_version(envelope.payload.version);
5174
5175 let (project_id, buffer) = this.update(&mut cx, |this, cx| {
5176 let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
5177 let buffer = this
5178 .opened_buffers
5179 .get(&buffer_id)
5180 .and_then(|buffer| buffer.upgrade(cx))
5181 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
5182 Ok::<_, anyhow::Error>((project_id, buffer))
5183 })?;
5184 buffer
5185 .update(&mut cx, |buffer, _| {
5186 buffer.wait_for_version(requested_version)
5187 })
5188 .await;
5189
5190 let (saved_version, fingerprint, mtime) =
5191 buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?;
5192 Ok(proto::BufferSaved {
5193 project_id,
5194 buffer_id,
5195 version: serialize_version(&saved_version),
5196 mtime: Some(mtime.into()),
5197 fingerprint,
5198 })
5199 }
5200
5201 async fn handle_reload_buffers(
5202 this: ModelHandle<Self>,
5203 envelope: TypedEnvelope<proto::ReloadBuffers>,
5204 _: Arc<Client>,
5205 mut cx: AsyncAppContext,
5206 ) -> Result<proto::ReloadBuffersResponse> {
5207 let sender_id = envelope.original_sender_id()?;
5208 let reload = this.update(&mut cx, |this, cx| {
5209 let mut buffers = HashSet::default();
5210 for buffer_id in &envelope.payload.buffer_ids {
5211 buffers.insert(
5212 this.opened_buffers
5213 .get(buffer_id)
5214 .and_then(|buffer| buffer.upgrade(cx))
5215 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
5216 );
5217 }
5218 Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
5219 })?;
5220
5221 let project_transaction = reload.await?;
5222 let project_transaction = this.update(&mut cx, |this, cx| {
5223 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
5224 });
5225 Ok(proto::ReloadBuffersResponse {
5226 transaction: Some(project_transaction),
5227 })
5228 }
5229
5230 async fn handle_format_buffers(
5231 this: ModelHandle<Self>,
5232 envelope: TypedEnvelope<proto::FormatBuffers>,
5233 _: Arc<Client>,
5234 mut cx: AsyncAppContext,
5235 ) -> Result<proto::FormatBuffersResponse> {
5236 let sender_id = envelope.original_sender_id()?;
5237 let format = this.update(&mut cx, |this, cx| {
5238 let mut buffers = HashSet::default();
5239 for buffer_id in &envelope.payload.buffer_ids {
5240 buffers.insert(
5241 this.opened_buffers
5242 .get(buffer_id)
5243 .and_then(|buffer| buffer.upgrade(cx))
5244 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
5245 );
5246 }
5247 Ok::<_, anyhow::Error>(this.format(buffers, false, cx))
5248 })?;
5249
5250 let project_transaction = format.await?;
5251 let project_transaction = this.update(&mut cx, |this, cx| {
5252 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
5253 });
5254 Ok(proto::FormatBuffersResponse {
5255 transaction: Some(project_transaction),
5256 })
5257 }
5258
5259 async fn handle_get_completions(
5260 this: ModelHandle<Self>,
5261 envelope: TypedEnvelope<proto::GetCompletions>,
5262 _: Arc<Client>,
5263 mut cx: AsyncAppContext,
5264 ) -> Result<proto::GetCompletionsResponse> {
5265 let position = envelope
5266 .payload
5267 .position
5268 .and_then(language::proto::deserialize_anchor)
5269 .ok_or_else(|| anyhow!("invalid position"))?;
5270 let version = deserialize_version(envelope.payload.version);
5271 let buffer = this.read_with(&cx, |this, cx| {
5272 this.opened_buffers
5273 .get(&envelope.payload.buffer_id)
5274 .and_then(|buffer| buffer.upgrade(cx))
5275 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
5276 })?;
5277 buffer
5278 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
5279 .await;
5280 let version = buffer.read_with(&cx, |buffer, _| buffer.version());
5281 let completions = this
5282 .update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
5283 .await?;
5284
5285 Ok(proto::GetCompletionsResponse {
5286 completions: completions
5287 .iter()
5288 .map(language::proto::serialize_completion)
5289 .collect(),
5290 version: serialize_version(&version),
5291 })
5292 }
5293
5294 async fn handle_apply_additional_edits_for_completion(
5295 this: ModelHandle<Self>,
5296 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
5297 _: Arc<Client>,
5298 mut cx: AsyncAppContext,
5299 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
5300 let (buffer, completion) = this.update(&mut cx, |this, cx| {
5301 let buffer = this
5302 .opened_buffers
5303 .get(&envelope.payload.buffer_id)
5304 .and_then(|buffer| buffer.upgrade(cx))
5305 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
5306 let language = buffer.read(cx).language();
5307 let completion = language::proto::deserialize_completion(
5308 envelope
5309 .payload
5310 .completion
5311 .ok_or_else(|| anyhow!("invalid completion"))?,
5312 language.cloned(),
5313 );
5314 Ok::<_, anyhow::Error>((buffer, completion))
5315 })?;
5316
5317 let completion = completion.await?;
5318
5319 let apply_additional_edits = this.update(&mut cx, |this, cx| {
5320 this.apply_additional_edits_for_completion(buffer, completion, false, cx)
5321 });
5322
5323 Ok(proto::ApplyCompletionAdditionalEditsResponse {
5324 transaction: apply_additional_edits
5325 .await?
5326 .as_ref()
5327 .map(language::proto::serialize_transaction),
5328 })
5329 }
5330
5331 async fn handle_get_code_actions(
5332 this: ModelHandle<Self>,
5333 envelope: TypedEnvelope<proto::GetCodeActions>,
5334 _: Arc<Client>,
5335 mut cx: AsyncAppContext,
5336 ) -> Result<proto::GetCodeActionsResponse> {
5337 let start = envelope
5338 .payload
5339 .start
5340 .and_then(language::proto::deserialize_anchor)
5341 .ok_or_else(|| anyhow!("invalid start"))?;
5342 let end = envelope
5343 .payload
5344 .end
5345 .and_then(language::proto::deserialize_anchor)
5346 .ok_or_else(|| anyhow!("invalid end"))?;
5347 let buffer = this.update(&mut cx, |this, cx| {
5348 this.opened_buffers
5349 .get(&envelope.payload.buffer_id)
5350 .and_then(|buffer| buffer.upgrade(cx))
5351 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
5352 })?;
5353 buffer
5354 .update(&mut cx, |buffer, _| {
5355 buffer.wait_for_version(deserialize_version(envelope.payload.version))
5356 })
5357 .await;
5358
5359 let version = buffer.read_with(&cx, |buffer, _| buffer.version());
5360 let code_actions = this.update(&mut cx, |this, cx| {
5361 Ok::<_, anyhow::Error>(this.code_actions(&buffer, start..end, cx))
5362 })?;
5363
5364 Ok(proto::GetCodeActionsResponse {
5365 actions: code_actions
5366 .await?
5367 .iter()
5368 .map(language::proto::serialize_code_action)
5369 .collect(),
5370 version: serialize_version(&version),
5371 })
5372 }
5373
5374 async fn handle_apply_code_action(
5375 this: ModelHandle<Self>,
5376 envelope: TypedEnvelope<proto::ApplyCodeAction>,
5377 _: Arc<Client>,
5378 mut cx: AsyncAppContext,
5379 ) -> Result<proto::ApplyCodeActionResponse> {
5380 let sender_id = envelope.original_sender_id()?;
5381 let action = language::proto::deserialize_code_action(
5382 envelope
5383 .payload
5384 .action
5385 .ok_or_else(|| anyhow!("invalid action"))?,
5386 )?;
5387 let apply_code_action = this.update(&mut cx, |this, cx| {
5388 let buffer = this
5389 .opened_buffers
5390 .get(&envelope.payload.buffer_id)
5391 .and_then(|buffer| buffer.upgrade(cx))
5392 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
5393 Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
5394 })?;
5395
5396 let project_transaction = apply_code_action.await?;
5397 let project_transaction = this.update(&mut cx, |this, cx| {
5398 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
5399 });
5400 Ok(proto::ApplyCodeActionResponse {
5401 transaction: Some(project_transaction),
5402 })
5403 }
5404
5405 async fn handle_lsp_command<T: LspCommand>(
5406 this: ModelHandle<Self>,
5407 envelope: TypedEnvelope<T::ProtoRequest>,
5408 _: Arc<Client>,
5409 mut cx: AsyncAppContext,
5410 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
5411 where
5412 <T::LspRequest as lsp::request::Request>::Result: Send,
5413 {
5414 let sender_id = envelope.original_sender_id()?;
5415 let buffer_id = T::buffer_id_from_proto(&envelope.payload);
5416 let buffer_handle = this.read_with(&cx, |this, _| {
5417 this.opened_buffers
5418 .get(&buffer_id)
5419 .and_then(|buffer| buffer.upgrade(&cx))
5420 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
5421 })?;
5422 let request = T::from_proto(
5423 envelope.payload,
5424 this.clone(),
5425 buffer_handle.clone(),
5426 cx.clone(),
5427 )
5428 .await?;
5429 let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version());
5430 let response = this
5431 .update(&mut cx, |this, cx| {
5432 this.request_lsp(buffer_handle, request, cx)
5433 })
5434 .await?;
5435 this.update(&mut cx, |this, cx| {
5436 Ok(T::response_to_proto(
5437 response,
5438 this,
5439 sender_id,
5440 &buffer_version,
5441 cx,
5442 ))
5443 })
5444 }
5445
5446 async fn handle_get_project_symbols(
5447 this: ModelHandle<Self>,
5448 envelope: TypedEnvelope<proto::GetProjectSymbols>,
5449 _: Arc<Client>,
5450 mut cx: AsyncAppContext,
5451 ) -> Result<proto::GetProjectSymbolsResponse> {
5452 let symbols = this
5453 .update(&mut cx, |this, cx| {
5454 this.symbols(&envelope.payload.query, cx)
5455 })
5456 .await?;
5457
5458 Ok(proto::GetProjectSymbolsResponse {
5459 symbols: symbols.iter().map(serialize_symbol).collect(),
5460 })
5461 }
5462
5463 async fn handle_search_project(
5464 this: ModelHandle<Self>,
5465 envelope: TypedEnvelope<proto::SearchProject>,
5466 _: Arc<Client>,
5467 mut cx: AsyncAppContext,
5468 ) -> Result<proto::SearchProjectResponse> {
5469 let peer_id = envelope.original_sender_id()?;
5470 let query = SearchQuery::from_proto(envelope.payload)?;
5471 let result = this
5472 .update(&mut cx, |this, cx| this.search(query, cx))
5473 .await?;
5474
5475 this.update(&mut cx, |this, cx| {
5476 let mut locations = Vec::new();
5477 for (buffer, ranges) in result {
5478 for range in ranges {
5479 let start = serialize_anchor(&range.start);
5480 let end = serialize_anchor(&range.end);
5481 let buffer = this.serialize_buffer_for_peer(&buffer, peer_id, cx);
5482 locations.push(proto::Location {
5483 buffer: Some(buffer),
5484 start: Some(start),
5485 end: Some(end),
5486 });
5487 }
5488 }
5489 Ok(proto::SearchProjectResponse { locations })
5490 })
5491 }
5492
5493 async fn handle_open_buffer_for_symbol(
5494 this: ModelHandle<Self>,
5495 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
5496 _: Arc<Client>,
5497 mut cx: AsyncAppContext,
5498 ) -> Result<proto::OpenBufferForSymbolResponse> {
5499 let peer_id = envelope.original_sender_id()?;
5500 let symbol = envelope
5501 .payload
5502 .symbol
5503 .ok_or_else(|| anyhow!("invalid symbol"))?;
5504 let symbol = this
5505 .read_with(&cx, |this, _| this.deserialize_symbol(symbol))
5506 .await?;
5507 let symbol = this.read_with(&cx, |this, _| {
5508 let signature = this.symbol_signature(symbol.worktree_id, &symbol.path);
5509 if signature == symbol.signature {
5510 Ok(symbol)
5511 } else {
5512 Err(anyhow!("invalid symbol signature"))
5513 }
5514 })?;
5515 let buffer = this
5516 .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))
5517 .await?;
5518
5519 Ok(proto::OpenBufferForSymbolResponse {
5520 buffer: Some(this.update(&mut cx, |this, cx| {
5521 this.serialize_buffer_for_peer(&buffer, peer_id, cx)
5522 })),
5523 })
5524 }
5525
5526 fn symbol_signature(&self, worktree_id: WorktreeId, path: &Path) -> [u8; 32] {
5527 let mut hasher = Sha256::new();
5528 hasher.update(worktree_id.to_proto().to_be_bytes());
5529 hasher.update(path.to_string_lossy().as_bytes());
5530 hasher.update(self.nonce.to_be_bytes());
5531 hasher.finalize().as_slice().try_into().unwrap()
5532 }
5533
5534 async fn handle_open_buffer_by_id(
5535 this: ModelHandle<Self>,
5536 envelope: TypedEnvelope<proto::OpenBufferById>,
5537 _: Arc<Client>,
5538 mut cx: AsyncAppContext,
5539 ) -> Result<proto::OpenBufferResponse> {
5540 let peer_id = envelope.original_sender_id()?;
5541 let buffer = this
5542 .update(&mut cx, |this, cx| {
5543 this.open_buffer_by_id(envelope.payload.id, cx)
5544 })
5545 .await?;
5546 this.update(&mut cx, |this, cx| {
5547 Ok(proto::OpenBufferResponse {
5548 buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
5549 })
5550 })
5551 }
5552
5553 async fn handle_open_buffer_by_path(
5554 this: ModelHandle<Self>,
5555 envelope: TypedEnvelope<proto::OpenBufferByPath>,
5556 _: Arc<Client>,
5557 mut cx: AsyncAppContext,
5558 ) -> Result<proto::OpenBufferResponse> {
5559 let peer_id = envelope.original_sender_id()?;
5560 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
5561 let open_buffer = this.update(&mut cx, |this, cx| {
5562 this.open_buffer(
5563 ProjectPath {
5564 worktree_id,
5565 path: PathBuf::from(envelope.payload.path).into(),
5566 },
5567 cx,
5568 )
5569 });
5570
5571 let buffer = open_buffer.await?;
5572 this.update(&mut cx, |this, cx| {
5573 Ok(proto::OpenBufferResponse {
5574 buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
5575 })
5576 })
5577 }
5578
5579 fn serialize_project_transaction_for_peer(
5580 &mut self,
5581 project_transaction: ProjectTransaction,
5582 peer_id: PeerId,
5583 cx: &AppContext,
5584 ) -> proto::ProjectTransaction {
5585 let mut serialized_transaction = proto::ProjectTransaction {
5586 buffers: Default::default(),
5587 transactions: Default::default(),
5588 };
5589 for (buffer, transaction) in project_transaction.0 {
5590 serialized_transaction
5591 .buffers
5592 .push(self.serialize_buffer_for_peer(&buffer, peer_id, cx));
5593 serialized_transaction
5594 .transactions
5595 .push(language::proto::serialize_transaction(&transaction));
5596 }
5597 serialized_transaction
5598 }
5599
5600 fn deserialize_project_transaction(
5601 &mut self,
5602 message: proto::ProjectTransaction,
5603 push_to_history: bool,
5604 cx: &mut ModelContext<Self>,
5605 ) -> Task<Result<ProjectTransaction>> {
5606 cx.spawn(|this, mut cx| async move {
5607 let mut project_transaction = ProjectTransaction::default();
5608 for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
5609 let buffer = this
5610 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
5611 .await?;
5612 let transaction = language::proto::deserialize_transaction(transaction)?;
5613 project_transaction.0.insert(buffer, transaction);
5614 }
5615
5616 for (buffer, transaction) in &project_transaction.0 {
5617 buffer
5618 .update(&mut cx, |buffer, _| {
5619 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
5620 })
5621 .await;
5622
5623 if push_to_history {
5624 buffer.update(&mut cx, |buffer, _| {
5625 buffer.push_transaction(transaction.clone(), Instant::now());
5626 });
5627 }
5628 }
5629
5630 Ok(project_transaction)
5631 })
5632 }
5633
5634 fn serialize_buffer_for_peer(
5635 &mut self,
5636 buffer: &ModelHandle<Buffer>,
5637 peer_id: PeerId,
5638 cx: &AppContext,
5639 ) -> proto::Buffer {
5640 let buffer_id = buffer.read(cx).remote_id();
5641 let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
5642 if shared_buffers.insert(buffer_id) {
5643 proto::Buffer {
5644 variant: Some(proto::buffer::Variant::State(buffer.read(cx).to_proto())),
5645 }
5646 } else {
5647 proto::Buffer {
5648 variant: Some(proto::buffer::Variant::Id(buffer_id)),
5649 }
5650 }
5651 }
5652
5653 fn deserialize_buffer(
5654 &mut self,
5655 buffer: proto::Buffer,
5656 cx: &mut ModelContext<Self>,
5657 ) -> Task<Result<ModelHandle<Buffer>>> {
5658 let replica_id = self.replica_id();
5659
5660 let opened_buffer_tx = self.opened_buffer.0.clone();
5661 let mut opened_buffer_rx = self.opened_buffer.1.clone();
5662 cx.spawn(|this, mut cx| async move {
5663 match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
5664 proto::buffer::Variant::Id(id) => {
5665 let buffer = loop {
5666 let buffer = this.read_with(&cx, |this, cx| {
5667 this.opened_buffers
5668 .get(&id)
5669 .and_then(|buffer| buffer.upgrade(cx))
5670 });
5671 if let Some(buffer) = buffer {
5672 break buffer;
5673 }
5674 opened_buffer_rx
5675 .next()
5676 .await
5677 .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
5678 };
5679 Ok(buffer)
5680 }
5681 proto::buffer::Variant::State(mut buffer) => {
5682 let mut buffer_worktree = None;
5683 let mut buffer_file = None;
5684 if let Some(file) = buffer.file.take() {
5685 this.read_with(&cx, |this, cx| {
5686 let worktree_id = WorktreeId::from_proto(file.worktree_id);
5687 let worktree =
5688 this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
5689 anyhow!("no worktree found for id {}", file.worktree_id)
5690 })?;
5691 buffer_file =
5692 Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
5693 as Arc<dyn language::File>);
5694 buffer_worktree = Some(worktree);
5695 Ok::<_, anyhow::Error>(())
5696 })?;
5697 }
5698
5699 let buffer = cx.add_model(|cx| {
5700 Buffer::from_proto(replica_id, buffer, buffer_file, cx).unwrap()
5701 });
5702
5703 this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
5704
5705 *opened_buffer_tx.borrow_mut().borrow_mut() = ();
5706 Ok(buffer)
5707 }
5708 }
5709 })
5710 }
5711
5712 fn deserialize_symbol(
5713 &self,
5714 serialized_symbol: proto::Symbol,
5715 ) -> impl Future<Output = Result<Symbol>> {
5716 let languages = self.languages.clone();
5717 async move {
5718 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
5719 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
5720 let start = serialized_symbol
5721 .start
5722 .ok_or_else(|| anyhow!("invalid start"))?;
5723 let end = serialized_symbol
5724 .end
5725 .ok_or_else(|| anyhow!("invalid end"))?;
5726 let kind = unsafe { mem::transmute(serialized_symbol.kind) };
5727 let path = PathBuf::from(serialized_symbol.path);
5728 let language = languages.select_language(&path);
5729 Ok(Symbol {
5730 source_worktree_id,
5731 worktree_id,
5732 language_server_name: LanguageServerName(
5733 serialized_symbol.language_server_name.into(),
5734 ),
5735 label: {
5736 match language {
5737 Some(language) => {
5738 language
5739 .label_for_symbol(&serialized_symbol.name, kind)
5740 .await
5741 }
5742 None => None,
5743 }
5744 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None))
5745 },
5746
5747 name: serialized_symbol.name,
5748 path,
5749 range: PointUtf16::new(start.row, start.column)
5750 ..PointUtf16::new(end.row, end.column),
5751 kind,
5752 signature: serialized_symbol
5753 .signature
5754 .try_into()
5755 .map_err(|_| anyhow!("invalid signature"))?,
5756 })
5757 }
5758 }
5759
5760 async fn handle_buffer_saved(
5761 this: ModelHandle<Self>,
5762 envelope: TypedEnvelope<proto::BufferSaved>,
5763 _: Arc<Client>,
5764 mut cx: AsyncAppContext,
5765 ) -> Result<()> {
5766 let version = deserialize_version(envelope.payload.version);
5767 let mtime = envelope
5768 .payload
5769 .mtime
5770 .ok_or_else(|| anyhow!("missing mtime"))?
5771 .into();
5772
5773 this.update(&mut cx, |this, cx| {
5774 let buffer = this
5775 .opened_buffers
5776 .get(&envelope.payload.buffer_id)
5777 .and_then(|buffer| buffer.upgrade(cx));
5778 if let Some(buffer) = buffer {
5779 buffer.update(cx, |buffer, cx| {
5780 buffer.did_save(version, envelope.payload.fingerprint, mtime, None, cx);
5781 });
5782 }
5783 Ok(())
5784 })
5785 }
5786
5787 async fn handle_buffer_reloaded(
5788 this: ModelHandle<Self>,
5789 envelope: TypedEnvelope<proto::BufferReloaded>,
5790 _: Arc<Client>,
5791 mut cx: AsyncAppContext,
5792 ) -> Result<()> {
5793 let payload = envelope.payload;
5794 let version = deserialize_version(payload.version);
5795 let line_ending = deserialize_line_ending(
5796 proto::LineEnding::from_i32(payload.line_ending)
5797 .ok_or_else(|| anyhow!("missing line ending"))?,
5798 );
5799 let mtime = payload
5800 .mtime
5801 .ok_or_else(|| anyhow!("missing mtime"))?
5802 .into();
5803 this.update(&mut cx, |this, cx| {
5804 let buffer = this
5805 .opened_buffers
5806 .get(&payload.buffer_id)
5807 .and_then(|buffer| buffer.upgrade(cx));
5808 if let Some(buffer) = buffer {
5809 buffer.update(cx, |buffer, cx| {
5810 buffer.did_reload(version, payload.fingerprint, line_ending, mtime, cx);
5811 });
5812 }
5813 Ok(())
5814 })
5815 }
5816
5817 pub fn match_paths<'a>(
5818 &self,
5819 query: &'a str,
5820 include_ignored: bool,
5821 smart_case: bool,
5822 max_results: usize,
5823 cancel_flag: &'a AtomicBool,
5824 cx: &AppContext,
5825 ) -> impl 'a + Future<Output = Vec<PathMatch>> {
5826 let worktrees = self
5827 .worktrees(cx)
5828 .filter(|worktree| worktree.read(cx).is_visible())
5829 .collect::<Vec<_>>();
5830 let include_root_name = worktrees.len() > 1;
5831 let candidate_sets = worktrees
5832 .into_iter()
5833 .map(|worktree| CandidateSet {
5834 snapshot: worktree.read(cx).snapshot(),
5835 include_ignored,
5836 include_root_name,
5837 })
5838 .collect::<Vec<_>>();
5839
5840 let background = cx.background().clone();
5841 async move {
5842 fuzzy::match_paths(
5843 candidate_sets.as_slice(),
5844 query,
5845 smart_case,
5846 max_results,
5847 cancel_flag,
5848 background,
5849 )
5850 .await
5851 }
5852 }
5853
5854 fn edits_from_lsp(
5855 &mut self,
5856 buffer: &ModelHandle<Buffer>,
5857 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
5858 version: Option<i32>,
5859 cx: &mut ModelContext<Self>,
5860 ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
5861 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, version, cx);
5862 cx.background().spawn(async move {
5863 let snapshot = snapshot?;
5864 let mut lsp_edits = lsp_edits
5865 .into_iter()
5866 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
5867 .collect::<Vec<_>>();
5868 lsp_edits.sort_by_key(|(range, _)| range.start);
5869
5870 let mut lsp_edits = lsp_edits.into_iter().peekable();
5871 let mut edits = Vec::new();
5872 while let Some((mut range, mut new_text)) = lsp_edits.next() {
5873 // Combine any LSP edits that are adjacent.
5874 //
5875 // Also, combine LSP edits that are separated from each other by only
5876 // a newline. This is important because for some code actions,
5877 // Rust-analyzer rewrites the entire buffer via a series of edits that
5878 // are separated by unchanged newline characters.
5879 //
5880 // In order for the diffing logic below to work properly, any edits that
5881 // cancel each other out must be combined into one.
5882 while let Some((next_range, next_text)) = lsp_edits.peek() {
5883 if next_range.start > range.end {
5884 if next_range.start.row > range.end.row + 1
5885 || next_range.start.column > 0
5886 || snapshot.clip_point_utf16(
5887 PointUtf16::new(range.end.row, u32::MAX),
5888 Bias::Left,
5889 ) > range.end
5890 {
5891 break;
5892 }
5893 new_text.push('\n');
5894 }
5895 range.end = next_range.end;
5896 new_text.push_str(&next_text);
5897 lsp_edits.next();
5898 }
5899
5900 if snapshot.clip_point_utf16(range.start, Bias::Left) != range.start
5901 || snapshot.clip_point_utf16(range.end, Bias::Left) != range.end
5902 {
5903 return Err(anyhow!("invalid edits received from language server"));
5904 }
5905
5906 // For multiline edits, perform a diff of the old and new text so that
5907 // we can identify the changes more precisely, preserving the locations
5908 // of any anchors positioned in the unchanged regions.
5909 if range.end.row > range.start.row {
5910 let mut offset = range.start.to_offset(&snapshot);
5911 let old_text = snapshot.text_for_range(range).collect::<String>();
5912
5913 let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
5914 let mut moved_since_edit = true;
5915 for change in diff.iter_all_changes() {
5916 let tag = change.tag();
5917 let value = change.value();
5918 match tag {
5919 ChangeTag::Equal => {
5920 offset += value.len();
5921 moved_since_edit = true;
5922 }
5923 ChangeTag::Delete => {
5924 let start = snapshot.anchor_after(offset);
5925 let end = snapshot.anchor_before(offset + value.len());
5926 if moved_since_edit {
5927 edits.push((start..end, String::new()));
5928 } else {
5929 edits.last_mut().unwrap().0.end = end;
5930 }
5931 offset += value.len();
5932 moved_since_edit = false;
5933 }
5934 ChangeTag::Insert => {
5935 if moved_since_edit {
5936 let anchor = snapshot.anchor_after(offset);
5937 edits.push((anchor.clone()..anchor, value.to_string()));
5938 } else {
5939 edits.last_mut().unwrap().1.push_str(value);
5940 }
5941 moved_since_edit = false;
5942 }
5943 }
5944 }
5945 } else if range.end == range.start {
5946 let anchor = snapshot.anchor_after(range.start);
5947 edits.push((anchor.clone()..anchor, new_text));
5948 } else {
5949 let edit_start = snapshot.anchor_after(range.start);
5950 let edit_end = snapshot.anchor_before(range.end);
5951 edits.push((edit_start..edit_end, new_text));
5952 }
5953 }
5954
5955 Ok(edits)
5956 })
5957 }
5958
5959 fn buffer_snapshot_for_lsp_version(
5960 &mut self,
5961 buffer: &ModelHandle<Buffer>,
5962 version: Option<i32>,
5963 cx: &AppContext,
5964 ) -> Result<TextBufferSnapshot> {
5965 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
5966
5967 if let Some(version) = version {
5968 let buffer_id = buffer.read(cx).remote_id();
5969 let snapshots = self
5970 .buffer_snapshots
5971 .get_mut(&buffer_id)
5972 .ok_or_else(|| anyhow!("no snapshot found for buffer {}", buffer_id))?;
5973 let mut found_snapshot = None;
5974 snapshots.retain(|(snapshot_version, snapshot)| {
5975 if snapshot_version + OLD_VERSIONS_TO_RETAIN < version {
5976 false
5977 } else {
5978 if *snapshot_version == version {
5979 found_snapshot = Some(snapshot.clone());
5980 }
5981 true
5982 }
5983 });
5984
5985 found_snapshot.ok_or_else(|| {
5986 anyhow!(
5987 "snapshot not found for buffer {} at version {}",
5988 buffer_id,
5989 version
5990 )
5991 })
5992 } else {
5993 Ok((buffer.read(cx)).text_snapshot())
5994 }
5995 }
5996
5997 fn language_server_for_buffer(
5998 &self,
5999 buffer: &Buffer,
6000 cx: &AppContext,
6001 ) -> Option<&(Arc<LspAdapter>, Arc<LanguageServer>)> {
6002 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
6003 let name = language.lsp_adapter()?.name.clone();
6004 let worktree_id = file.worktree_id(cx);
6005 let key = (worktree_id, language.lsp_adapter()?.name());
6006
6007 if let Some(server_id) = self.language_server_ids.get(&key) {
6008 if let Some(LanguageServerState::Running { adapter, server }) =
6009 self.language_servers.get(&server_id)
6010 {
6011 return Some((adapter, server));
6012 }
6013 }
6014 }
6015
6016 None
6017 }
6018}
6019
6020impl ProjectStore {
6021 pub fn new(db: Arc<Db>) -> Self {
6022 Self {
6023 db,
6024 projects: Default::default(),
6025 }
6026 }
6027
6028 pub fn projects<'a>(
6029 &'a self,
6030 cx: &'a AppContext,
6031 ) -> impl 'a + Iterator<Item = ModelHandle<Project>> {
6032 self.projects
6033 .iter()
6034 .filter_map(|project| project.upgrade(cx))
6035 }
6036
6037 fn add_project(&mut self, project: WeakModelHandle<Project>, cx: &mut ModelContext<Self>) {
6038 if let Err(ix) = self
6039 .projects
6040 .binary_search_by_key(&project.id(), WeakModelHandle::id)
6041 {
6042 self.projects.insert(ix, project);
6043 }
6044 cx.notify();
6045 }
6046
6047 fn prune_projects(&mut self, cx: &mut ModelContext<Self>) {
6048 let mut did_change = false;
6049 self.projects.retain(|project| {
6050 if project.is_upgradable(cx) {
6051 true
6052 } else {
6053 did_change = true;
6054 false
6055 }
6056 });
6057 if did_change {
6058 cx.notify();
6059 }
6060 }
6061}
6062
6063impl WorktreeHandle {
6064 pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
6065 match self {
6066 WorktreeHandle::Strong(handle) => Some(handle.clone()),
6067 WorktreeHandle::Weak(handle) => handle.upgrade(cx),
6068 }
6069 }
6070}
6071
6072impl OpenBuffer {
6073 pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
6074 match self {
6075 OpenBuffer::Strong(handle) => Some(handle.clone()),
6076 OpenBuffer::Weak(handle) => handle.upgrade(cx),
6077 OpenBuffer::Loading(_) => None,
6078 }
6079 }
6080}
6081
6082struct CandidateSet {
6083 snapshot: Snapshot,
6084 include_ignored: bool,
6085 include_root_name: bool,
6086}
6087
6088impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
6089 type Candidates = CandidateSetIter<'a>;
6090
6091 fn id(&self) -> usize {
6092 self.snapshot.id().to_usize()
6093 }
6094
6095 fn len(&self) -> usize {
6096 if self.include_ignored {
6097 self.snapshot.file_count()
6098 } else {
6099 self.snapshot.visible_file_count()
6100 }
6101 }
6102
6103 fn prefix(&self) -> Arc<str> {
6104 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
6105 self.snapshot.root_name().into()
6106 } else if self.include_root_name {
6107 format!("{}/", self.snapshot.root_name()).into()
6108 } else {
6109 "".into()
6110 }
6111 }
6112
6113 fn candidates(&'a self, start: usize) -> Self::Candidates {
6114 CandidateSetIter {
6115 traversal: self.snapshot.files(self.include_ignored, start),
6116 }
6117 }
6118}
6119
6120struct CandidateSetIter<'a> {
6121 traversal: Traversal<'a>,
6122}
6123
6124impl<'a> Iterator for CandidateSetIter<'a> {
6125 type Item = PathMatchCandidate<'a>;
6126
6127 fn next(&mut self) -> Option<Self::Item> {
6128 self.traversal.next().map(|entry| {
6129 if let EntryKind::File(char_bag) = entry.kind {
6130 PathMatchCandidate {
6131 path: &entry.path,
6132 char_bag,
6133 }
6134 } else {
6135 unreachable!()
6136 }
6137 })
6138 }
6139}
6140
6141impl Entity for ProjectStore {
6142 type Event = ();
6143}
6144
6145impl Entity for Project {
6146 type Event = Event;
6147
6148 fn release(&mut self, cx: &mut gpui::MutableAppContext) {
6149 self.project_store.update(cx, ProjectStore::prune_projects);
6150
6151 match &self.client_state {
6152 ProjectClientState::Local { remote_id_rx, .. } => {
6153 if let Some(project_id) = *remote_id_rx.borrow() {
6154 self.client
6155 .send(proto::UnregisterProject { project_id })
6156 .log_err();
6157 }
6158 }
6159 ProjectClientState::Remote { remote_id, .. } => {
6160 self.client
6161 .send(proto::LeaveProject {
6162 project_id: *remote_id,
6163 })
6164 .log_err();
6165 }
6166 }
6167 }
6168
6169 fn app_will_quit(
6170 &mut self,
6171 _: &mut MutableAppContext,
6172 ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
6173 let shutdown_futures = self
6174 .language_servers
6175 .drain()
6176 .map(|(_, server_state)| async {
6177 match server_state {
6178 LanguageServerState::Running { server, .. } => server.shutdown()?.await,
6179 LanguageServerState::Starting(starting_server) => {
6180 starting_server.await?.shutdown()?.await
6181 }
6182 }
6183 })
6184 .collect::<Vec<_>>();
6185
6186 Some(
6187 async move {
6188 futures::future::join_all(shutdown_futures).await;
6189 }
6190 .boxed(),
6191 )
6192 }
6193}
6194
6195impl Collaborator {
6196 fn from_proto(
6197 message: proto::Collaborator,
6198 user_store: &ModelHandle<UserStore>,
6199 cx: &mut AsyncAppContext,
6200 ) -> impl Future<Output = Result<Self>> {
6201 let user = user_store.update(cx, |user_store, cx| {
6202 user_store.fetch_user(message.user_id, cx)
6203 });
6204
6205 async move {
6206 Ok(Self {
6207 peer_id: PeerId(message.peer_id),
6208 user: user.await?,
6209 replica_id: message.replica_id as ReplicaId,
6210 })
6211 }
6212 }
6213}
6214
6215impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
6216 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
6217 Self {
6218 worktree_id,
6219 path: path.as_ref().into(),
6220 }
6221 }
6222}
6223
6224impl From<lsp::CreateFileOptions> for fs::CreateOptions {
6225 fn from(options: lsp::CreateFileOptions) -> Self {
6226 Self {
6227 overwrite: options.overwrite.unwrap_or(false),
6228 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
6229 }
6230 }
6231}
6232
6233impl From<lsp::RenameFileOptions> for fs::RenameOptions {
6234 fn from(options: lsp::RenameFileOptions) -> Self {
6235 Self {
6236 overwrite: options.overwrite.unwrap_or(false),
6237 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
6238 }
6239 }
6240}
6241
6242impl From<lsp::DeleteFileOptions> for fs::RemoveOptions {
6243 fn from(options: lsp::DeleteFileOptions) -> Self {
6244 Self {
6245 recursive: options.recursive.unwrap_or(false),
6246 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
6247 }
6248 }
6249}
6250
6251fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
6252 proto::Symbol {
6253 source_worktree_id: symbol.source_worktree_id.to_proto(),
6254 worktree_id: symbol.worktree_id.to_proto(),
6255 language_server_name: symbol.language_server_name.0.to_string(),
6256 name: symbol.name.clone(),
6257 kind: unsafe { mem::transmute(symbol.kind) },
6258 path: symbol.path.to_string_lossy().to_string(),
6259 start: Some(proto::Point {
6260 row: symbol.range.start.row,
6261 column: symbol.range.start.column,
6262 }),
6263 end: Some(proto::Point {
6264 row: symbol.range.end.row,
6265 column: symbol.range.end.column,
6266 }),
6267 signature: symbol.signature.to_vec(),
6268 }
6269}
6270
6271fn relativize_path(base: &Path, path: &Path) -> PathBuf {
6272 let mut path_components = path.components();
6273 let mut base_components = base.components();
6274 let mut components: Vec<Component> = Vec::new();
6275 loop {
6276 match (path_components.next(), base_components.next()) {
6277 (None, None) => break,
6278 (Some(a), None) => {
6279 components.push(a);
6280 components.extend(path_components.by_ref());
6281 break;
6282 }
6283 (None, _) => components.push(Component::ParentDir),
6284 (Some(a), Some(b)) if components.is_empty() && a == b => (),
6285 (Some(a), Some(b)) if b == Component::CurDir => components.push(a),
6286 (Some(a), Some(_)) => {
6287 components.push(Component::ParentDir);
6288 for _ in base_components {
6289 components.push(Component::ParentDir);
6290 }
6291 components.push(a);
6292 components.extend(path_components.by_ref());
6293 break;
6294 }
6295 }
6296 }
6297 components.iter().map(|c| c.as_os_str()).collect()
6298}
6299
6300impl Item for Buffer {
6301 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
6302 File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
6303 }
6304}