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