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