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