1pub mod debounced_delay;
2pub mod lsp_command;
3pub mod lsp_ext_command;
4mod prettier_support;
5pub mod search;
6mod task_inventory;
7pub mod terminals;
8
9#[cfg(test)]
10mod project_tests;
11
12use anyhow::{anyhow, bail, Context as _, Result};
13use client::{proto, Client, Collaborator, TypedEnvelope, UserStore};
14use clock::ReplicaId;
15use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque};
16use copilot::Copilot;
17use debounced_delay::DebouncedDelay;
18use futures::{
19 channel::mpsc::{self, UnboundedReceiver},
20 future::{try_join_all, Shared},
21 select,
22 stream::FuturesUnordered,
23 AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt,
24};
25use globset::{Glob, GlobSet, GlobSetBuilder};
26use gpui::{
27 AnyModel, AppContext, AsyncAppContext, BackgroundExecutor, Context, Entity, EventEmitter,
28 Model, ModelContext, PromptLevel, Task, WeakModel,
29};
30use itertools::Itertools;
31use language::{
32 language_settings::{language_settings, FormatOnSave, Formatter, InlayHintKind},
33 markdown, point_to_lsp,
34 proto::{
35 deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
36 serialize_anchor, serialize_version, split_operations,
37 },
38 range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability,
39 CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
40 Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName,
41 LocalFile, LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer,
42 PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
43};
44use log::error;
45use lsp::{
46 DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
47 DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId,
48 MessageActionItem, OneOf,
49};
50use lsp_command::*;
51use node_runtime::NodeRuntime;
52use parking_lot::{Mutex, RwLock};
53use postage::watch;
54use prettier_support::{DefaultPrettier, PrettierInstance};
55use project_core::project_settings::{LspSettings, ProjectSettings};
56pub use project_core::{DiagnosticSummary, ProjectEntryId};
57use rand::prelude::*;
58
59use rpc::{ErrorCode, ErrorExt as _};
60use search::SearchQuery;
61use serde::Serialize;
62use settings::{watch_config_file, Settings, SettingsStore};
63use sha2::{Digest, Sha256};
64use similar::{ChangeTag, TextDiff};
65use smol::channel::{Receiver, Sender};
66use smol::lock::Semaphore;
67use std::{
68 cmp::{self, Ordering},
69 convert::TryInto,
70 env,
71 ffi::OsString,
72 hash::Hash,
73 mem,
74 num::NonZeroU32,
75 ops::Range,
76 path::{self, Component, Path, PathBuf},
77 process::Stdio,
78 str,
79 sync::{
80 atomic::{AtomicUsize, Ordering::SeqCst},
81 Arc,
82 },
83 time::{Duration, Instant},
84};
85use task::static_source::StaticSource;
86use terminals::Terminals;
87use text::{Anchor, BufferId};
88use util::{
89 debug_panic, defer,
90 http::HttpClient,
91 merge_json_value_into,
92 paths::{LOCAL_SETTINGS_RELATIVE_PATH, LOCAL_TASKS_RELATIVE_PATH},
93 post_inc, ResultExt, TryFutureExt as _,
94};
95
96pub use fs::*;
97pub use language::Location;
98#[cfg(any(test, feature = "test-support"))]
99pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
100pub use project_core::project_settings;
101pub use project_core::worktree::{self, *};
102pub use task_inventory::{Inventory, TaskSourceKind};
103
104const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
105const SERVER_REINSTALL_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
106const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
107
108pub trait Item {
109 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
110 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
111}
112
113pub struct Project {
114 worktrees: Vec<WorktreeHandle>,
115 active_entry: Option<ProjectEntryId>,
116 buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
117 languages: Arc<LanguageRegistry>,
118 supplementary_language_servers:
119 HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
120 language_servers: HashMap<LanguageServerId, LanguageServerState>,
121 language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
122 language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
123 last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
124 client: Arc<client::Client>,
125 next_entry_id: Arc<AtomicUsize>,
126 join_project_response_message_id: u32,
127 next_diagnostic_group_id: usize,
128 user_store: Model<UserStore>,
129 fs: Arc<dyn Fs>,
130 client_state: ProjectClientState,
131 collaborators: HashMap<proto::PeerId, Collaborator>,
132 client_subscriptions: Vec<client::Subscription>,
133 _subscriptions: Vec<gpui::Subscription>,
134 next_buffer_id: BufferId,
135 opened_buffer: (watch::Sender<()>, watch::Receiver<()>),
136 shared_buffers: HashMap<proto::PeerId, HashSet<BufferId>>,
137 #[allow(clippy::type_complexity)]
138 loading_buffers_by_path: HashMap<
139 ProjectPath,
140 postage::watch::Receiver<Option<Result<Model<Buffer>, Arc<anyhow::Error>>>>,
141 >,
142 #[allow(clippy::type_complexity)]
143 loading_local_worktrees:
144 HashMap<Arc<Path>, Shared<Task<Result<Model<Worktree>, Arc<anyhow::Error>>>>>,
145 opened_buffers: HashMap<BufferId, OpenBuffer>,
146 local_buffer_ids_by_path: HashMap<ProjectPath, BufferId>,
147 local_buffer_ids_by_entry_id: HashMap<ProjectEntryId, BufferId>,
148 /// A mapping from a buffer ID to None means that we've started waiting for an ID but haven't finished loading it.
149 /// Used for re-issuing buffer requests when peers temporarily disconnect
150 incomplete_remote_buffers: HashMap<BufferId, Option<Model<Buffer>>>,
151 buffer_snapshots: HashMap<BufferId, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots
152 buffers_being_formatted: HashSet<BufferId>,
153 buffers_needing_diff: HashSet<WeakModel<Buffer>>,
154 git_diff_debouncer: DebouncedDelay,
155 nonce: u128,
156 _maintain_buffer_languages: Task<()>,
157 _maintain_workspace_config: Task<Result<()>>,
158 terminals: Terminals,
159 copilot_lsp_subscription: Option<gpui::Subscription>,
160 copilot_log_subscription: Option<lsp::Subscription>,
161 current_lsp_settings: HashMap<Arc<str>, LspSettings>,
162 node: Option<Arc<dyn NodeRuntime>>,
163 default_prettier: DefaultPrettier,
164 prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
165 prettier_instances: HashMap<PathBuf, PrettierInstance>,
166 tasks: Model<Inventory>,
167}
168
169pub enum LanguageServerToQuery {
170 Primary,
171 Other(LanguageServerId),
172}
173
174struct LspBufferSnapshot {
175 version: i32,
176 snapshot: TextBufferSnapshot,
177}
178
179/// Message ordered with respect to buffer operations
180enum BufferOrderedMessage {
181 Operation {
182 buffer_id: BufferId,
183 operation: proto::Operation,
184 },
185 LanguageServerUpdate {
186 language_server_id: LanguageServerId,
187 message: proto::update_language_server::Variant,
188 },
189 Resync,
190}
191
192enum LocalProjectUpdate {
193 WorktreesChanged,
194 CreateBufferForPeer {
195 peer_id: proto::PeerId,
196 buffer_id: BufferId,
197 },
198}
199
200enum OpenBuffer {
201 Strong(Model<Buffer>),
202 Weak(WeakModel<Buffer>),
203 Operations(Vec<Operation>),
204}
205
206#[derive(Clone)]
207enum WorktreeHandle {
208 Strong(Model<Worktree>),
209 Weak(WeakModel<Worktree>),
210}
211
212#[derive(Debug)]
213enum ProjectClientState {
214 Local,
215 Shared {
216 remote_id: u64,
217 updates_tx: mpsc::UnboundedSender<LocalProjectUpdate>,
218 _send_updates: Task<Result<()>>,
219 },
220 Remote {
221 sharing_has_stopped: bool,
222 capability: Capability,
223 remote_id: u64,
224 replica_id: ReplicaId,
225 },
226}
227
228/// A prompt requested by LSP server.
229#[derive(Clone, Debug)]
230pub struct LanguageServerPromptRequest {
231 pub level: PromptLevel,
232 pub message: String,
233 pub actions: Vec<MessageActionItem>,
234 pub lsp_name: String,
235 response_channel: Sender<MessageActionItem>,
236}
237
238impl LanguageServerPromptRequest {
239 pub async fn respond(self, index: usize) -> Option<()> {
240 if let Some(response) = self.actions.into_iter().nth(index) {
241 self.response_channel.send(response).await.ok()
242 } else {
243 None
244 }
245 }
246}
247impl PartialEq for LanguageServerPromptRequest {
248 fn eq(&self, other: &Self) -> bool {
249 self.message == other.message && self.actions == other.actions
250 }
251}
252
253#[derive(Clone, Debug, PartialEq)]
254pub enum Event {
255 LanguageServerAdded(LanguageServerId),
256 LanguageServerRemoved(LanguageServerId),
257 LanguageServerLog(LanguageServerId, String),
258 Notification(String),
259 LanguageServerPrompt(LanguageServerPromptRequest),
260 ActiveEntryChanged(Option<ProjectEntryId>),
261 ActivateProjectPanel,
262 WorktreeAdded,
263 WorktreeRemoved(WorktreeId),
264 WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
265 DiskBasedDiagnosticsStarted {
266 language_server_id: LanguageServerId,
267 },
268 DiskBasedDiagnosticsFinished {
269 language_server_id: LanguageServerId,
270 },
271 DiagnosticsUpdated {
272 path: ProjectPath,
273 language_server_id: LanguageServerId,
274 },
275 RemoteIdChanged(Option<u64>),
276 DisconnectedFromHost,
277 Closed,
278 DeletedEntry(ProjectEntryId),
279 CollaboratorUpdated {
280 old_peer_id: proto::PeerId,
281 new_peer_id: proto::PeerId,
282 },
283 CollaboratorJoined(proto::PeerId),
284 CollaboratorLeft(proto::PeerId),
285 RefreshInlayHints,
286 RevealInProjectPanel(ProjectEntryId),
287}
288
289pub enum LanguageServerState {
290 Starting(Task<Option<Arc<LanguageServer>>>),
291
292 Running {
293 language: Arc<Language>,
294 adapter: Arc<CachedLspAdapter>,
295 server: Arc<LanguageServer>,
296 watched_paths: HashMap<WorktreeId, GlobSet>,
297 simulate_disk_based_diagnostics_completion: Option<Task<()>>,
298 },
299}
300
301#[derive(Serialize)]
302pub struct LanguageServerStatus {
303 pub name: String,
304 pub pending_work: BTreeMap<String, LanguageServerProgress>,
305 pub has_pending_diagnostic_updates: bool,
306 progress_tokens: HashSet<String>,
307}
308
309#[derive(Clone, Debug, Serialize)]
310pub struct LanguageServerProgress {
311 pub message: Option<String>,
312 pub percentage: Option<usize>,
313 #[serde(skip_serializing)]
314 pub last_update_at: Instant,
315}
316
317#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
318pub struct ProjectPath {
319 pub worktree_id: WorktreeId,
320 pub path: Arc<Path>,
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
324pub struct InlayHint {
325 pub position: language::Anchor,
326 pub label: InlayHintLabel,
327 pub kind: Option<InlayHintKind>,
328 pub padding_left: bool,
329 pub padding_right: bool,
330 pub tooltip: Option<InlayHintTooltip>,
331 pub resolve_state: ResolveState,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub enum ResolveState {
336 Resolved,
337 CanResolve(LanguageServerId, Option<lsp::LSPAny>),
338 Resolving,
339}
340
341impl InlayHint {
342 pub fn text(&self) -> String {
343 match &self.label {
344 InlayHintLabel::String(s) => s.to_owned(),
345 InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
346 }
347 }
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub enum InlayHintLabel {
352 String(String),
353 LabelParts(Vec<InlayHintLabelPart>),
354}
355
356#[derive(Debug, Clone, PartialEq, Eq)]
357pub struct InlayHintLabelPart {
358 pub value: String,
359 pub tooltip: Option<InlayHintLabelPartTooltip>,
360 pub location: Option<(LanguageServerId, lsp::Location)>,
361}
362
363#[derive(Debug, Clone, PartialEq, Eq)]
364pub enum InlayHintTooltip {
365 String(String),
366 MarkupContent(MarkupContent),
367}
368
369#[derive(Debug, Clone, PartialEq, Eq)]
370pub enum InlayHintLabelPartTooltip {
371 String(String),
372 MarkupContent(MarkupContent),
373}
374
375#[derive(Debug, Clone, PartialEq, Eq)]
376pub struct MarkupContent {
377 pub kind: HoverBlockKind,
378 pub value: String,
379}
380
381#[derive(Debug, Clone)]
382pub struct LocationLink {
383 pub origin: Option<Location>,
384 pub target: Location,
385}
386
387#[derive(Debug)]
388pub struct DocumentHighlight {
389 pub range: Range<language::Anchor>,
390 pub kind: DocumentHighlightKind,
391}
392
393#[derive(Clone, Debug)]
394pub struct Symbol {
395 pub language_server_name: LanguageServerName,
396 pub source_worktree_id: WorktreeId,
397 pub path: ProjectPath,
398 pub label: CodeLabel,
399 pub name: String,
400 pub kind: lsp::SymbolKind,
401 pub range: Range<Unclipped<PointUtf16>>,
402 pub signature: [u8; 32],
403}
404
405#[derive(Clone, Debug, PartialEq)]
406pub struct HoverBlock {
407 pub text: String,
408 pub kind: HoverBlockKind,
409}
410
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub enum HoverBlockKind {
413 PlainText,
414 Markdown,
415 Code { language: String },
416}
417
418#[derive(Debug)]
419pub struct Hover {
420 pub contents: Vec<HoverBlock>,
421 pub range: Option<Range<language::Anchor>>,
422 pub language: Option<Arc<Language>>,
423}
424
425impl Hover {
426 pub fn is_empty(&self) -> bool {
427 self.contents.iter().all(|block| block.text.is_empty())
428 }
429}
430
431#[derive(Default)]
432pub struct ProjectTransaction(pub HashMap<Model<Buffer>, language::Transaction>);
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
435pub enum FormatTrigger {
436 Save,
437 Manual,
438}
439
440// Currently, formatting operations are represented differently depending on
441// whether they come from a language server or an external command.
442enum FormatOperation {
443 Lsp(Vec<(Range<Anchor>, String)>),
444 External(Diff),
445 Prettier(Diff),
446}
447
448impl FormatTrigger {
449 fn from_proto(value: i32) -> FormatTrigger {
450 match value {
451 0 => FormatTrigger::Save,
452 1 => FormatTrigger::Manual,
453 _ => FormatTrigger::Save,
454 }
455 }
456}
457#[derive(Clone, Debug, PartialEq)]
458enum SearchMatchCandidate {
459 OpenBuffer {
460 buffer: Model<Buffer>,
461 // This might be an unnamed file without representation on filesystem
462 path: Option<Arc<Path>>,
463 },
464 Path {
465 worktree_id: WorktreeId,
466 is_ignored: bool,
467 path: Arc<Path>,
468 },
469}
470
471type SearchMatchCandidateIndex = usize;
472impl SearchMatchCandidate {
473 fn path(&self) -> Option<Arc<Path>> {
474 match self {
475 SearchMatchCandidate::OpenBuffer { path, .. } => path.clone(),
476 SearchMatchCandidate::Path { path, .. } => Some(path.clone()),
477 }
478 }
479}
480
481impl Project {
482 pub fn init_settings(cx: &mut AppContext) {
483 ProjectSettings::register(cx);
484 }
485
486 pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
487 Self::init_settings(cx);
488
489 client.add_model_message_handler(Self::handle_add_collaborator);
490 client.add_model_message_handler(Self::handle_update_project_collaborator);
491 client.add_model_message_handler(Self::handle_remove_collaborator);
492 client.add_model_message_handler(Self::handle_buffer_reloaded);
493 client.add_model_message_handler(Self::handle_buffer_saved);
494 client.add_model_message_handler(Self::handle_start_language_server);
495 client.add_model_message_handler(Self::handle_update_language_server);
496 client.add_model_message_handler(Self::handle_update_project);
497 client.add_model_message_handler(Self::handle_unshare_project);
498 client.add_model_message_handler(Self::handle_create_buffer_for_peer);
499 client.add_model_message_handler(Self::handle_update_buffer_file);
500 client.add_model_request_handler(Self::handle_update_buffer);
501 client.add_model_message_handler(Self::handle_update_diagnostic_summary);
502 client.add_model_message_handler(Self::handle_update_worktree);
503 client.add_model_message_handler(Self::handle_update_worktree_settings);
504 client.add_model_request_handler(Self::handle_create_project_entry);
505 client.add_model_request_handler(Self::handle_rename_project_entry);
506 client.add_model_request_handler(Self::handle_copy_project_entry);
507 client.add_model_request_handler(Self::handle_delete_project_entry);
508 client.add_model_request_handler(Self::handle_expand_project_entry);
509 client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
510 client.add_model_request_handler(Self::handle_resolve_completion_documentation);
511 client.add_model_request_handler(Self::handle_apply_code_action);
512 client.add_model_request_handler(Self::handle_on_type_formatting);
513 client.add_model_request_handler(Self::handle_inlay_hints);
514 client.add_model_request_handler(Self::handle_resolve_inlay_hint);
515 client.add_model_request_handler(Self::handle_refresh_inlay_hints);
516 client.add_model_request_handler(Self::handle_reload_buffers);
517 client.add_model_request_handler(Self::handle_synchronize_buffers);
518 client.add_model_request_handler(Self::handle_format_buffers);
519 client.add_model_request_handler(Self::handle_lsp_command::<GetCodeActions>);
520 client.add_model_request_handler(Self::handle_lsp_command::<GetCompletions>);
521 client.add_model_request_handler(Self::handle_lsp_command::<GetHover>);
522 client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
523 client.add_model_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
524 client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
525 client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
526 client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
527 client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
528 client.add_model_request_handler(Self::handle_search_project);
529 client.add_model_request_handler(Self::handle_get_project_symbols);
530 client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
531 client.add_model_request_handler(Self::handle_open_buffer_by_id);
532 client.add_model_request_handler(Self::handle_open_buffer_by_path);
533 client.add_model_request_handler(Self::handle_save_buffer);
534 client.add_model_message_handler(Self::handle_update_diff_base);
535 client.add_model_request_handler(Self::handle_lsp_command::<lsp_ext_command::ExpandMacro>);
536 }
537
538 pub fn local(
539 client: Arc<Client>,
540 node: Arc<dyn NodeRuntime>,
541 user_store: Model<UserStore>,
542 languages: Arc<LanguageRegistry>,
543 fs: Arc<dyn Fs>,
544 cx: &mut AppContext,
545 ) -> Model<Self> {
546 cx.new_model(|cx: &mut ModelContext<Self>| {
547 let (tx, rx) = mpsc::unbounded();
548 cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
549 .detach();
550 let copilot_lsp_subscription =
551 Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx));
552 let tasks = Inventory::new(cx);
553
554 Self {
555 worktrees: Vec::new(),
556 buffer_ordered_messages_tx: tx,
557 collaborators: Default::default(),
558 next_buffer_id: BufferId::new(1).unwrap(),
559 opened_buffers: Default::default(),
560 shared_buffers: Default::default(),
561 incomplete_remote_buffers: Default::default(),
562 loading_buffers_by_path: Default::default(),
563 loading_local_worktrees: Default::default(),
564 local_buffer_ids_by_path: Default::default(),
565 local_buffer_ids_by_entry_id: Default::default(),
566 buffer_snapshots: Default::default(),
567 join_project_response_message_id: 0,
568 client_state: ProjectClientState::Local,
569 opened_buffer: watch::channel(),
570 client_subscriptions: Vec::new(),
571 _subscriptions: vec![
572 cx.observe_global::<SettingsStore>(Self::on_settings_changed),
573 cx.on_release(Self::release),
574 cx.on_app_quit(Self::shutdown_language_servers),
575 ],
576 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
577 _maintain_workspace_config: Self::maintain_workspace_config(cx),
578 active_entry: None,
579 languages,
580 client,
581 user_store,
582 fs,
583 next_entry_id: Default::default(),
584 next_diagnostic_group_id: Default::default(),
585 supplementary_language_servers: HashMap::default(),
586 language_servers: Default::default(),
587 language_server_ids: HashMap::default(),
588 language_server_statuses: Default::default(),
589 last_workspace_edits_by_language_server: Default::default(),
590 buffers_being_formatted: Default::default(),
591 buffers_needing_diff: Default::default(),
592 git_diff_debouncer: DebouncedDelay::new(),
593 nonce: StdRng::from_entropy().gen(),
594 terminals: Terminals {
595 local_handles: Vec::new(),
596 },
597 copilot_lsp_subscription,
598 copilot_log_subscription: None,
599 current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
600 node: Some(node),
601 default_prettier: DefaultPrettier::default(),
602 prettiers_per_worktree: HashMap::default(),
603 prettier_instances: HashMap::default(),
604 tasks,
605 }
606 })
607 }
608
609 pub async fn remote(
610 remote_id: u64,
611 client: Arc<Client>,
612 user_store: Model<UserStore>,
613 languages: Arc<LanguageRegistry>,
614 fs: Arc<dyn Fs>,
615 role: proto::ChannelRole,
616 mut cx: AsyncAppContext,
617 ) -> Result<Model<Self>> {
618 client.authenticate_and_connect(true, &cx).await?;
619
620 let subscription = client.subscribe_to_entity(remote_id)?;
621 let response = client
622 .request_envelope(proto::JoinProject {
623 project_id: remote_id,
624 })
625 .await?;
626 let this = cx.new_model(|cx| {
627 let replica_id = response.payload.replica_id as ReplicaId;
628 let tasks = Inventory::new(cx);
629 // BIG CAUTION NOTE: The order in which we initialize fields here matters and it should match what's done in Self::local.
630 // Otherwise, you might run into issues where worktree id on remote is different than what's on local host.
631 // That's because Worktree's identifier is entity id, which should probably be changed.
632 let mut worktrees = Vec::new();
633 for worktree in response.payload.worktrees {
634 let worktree =
635 Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx);
636 worktrees.push(worktree);
637 }
638
639 let (tx, rx) = mpsc::unbounded();
640 cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
641 .detach();
642 let copilot_lsp_subscription =
643 Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx));
644 let mut this = Self {
645 worktrees: Vec::new(),
646 buffer_ordered_messages_tx: tx,
647 loading_buffers_by_path: Default::default(),
648 next_buffer_id: BufferId::new(1).unwrap(),
649 opened_buffer: watch::channel(),
650 shared_buffers: Default::default(),
651 incomplete_remote_buffers: Default::default(),
652 loading_local_worktrees: Default::default(),
653 local_buffer_ids_by_path: Default::default(),
654 local_buffer_ids_by_entry_id: Default::default(),
655 active_entry: None,
656 collaborators: Default::default(),
657 join_project_response_message_id: response.message_id,
658 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
659 _maintain_workspace_config: Self::maintain_workspace_config(cx),
660 languages,
661 user_store: user_store.clone(),
662 fs,
663 next_entry_id: Default::default(),
664 next_diagnostic_group_id: Default::default(),
665 client_subscriptions: Default::default(),
666 _subscriptions: vec![
667 cx.on_release(Self::release),
668 cx.on_app_quit(Self::shutdown_language_servers),
669 ],
670 client: client.clone(),
671 client_state: ProjectClientState::Remote {
672 sharing_has_stopped: false,
673 capability: Capability::ReadWrite,
674 remote_id,
675 replica_id,
676 },
677 supplementary_language_servers: HashMap::default(),
678 language_servers: Default::default(),
679 language_server_ids: HashMap::default(),
680 language_server_statuses: response
681 .payload
682 .language_servers
683 .into_iter()
684 .map(|server| {
685 (
686 LanguageServerId(server.id as usize),
687 LanguageServerStatus {
688 name: server.name,
689 pending_work: Default::default(),
690 has_pending_diagnostic_updates: false,
691 progress_tokens: Default::default(),
692 },
693 )
694 })
695 .collect(),
696 last_workspace_edits_by_language_server: Default::default(),
697 opened_buffers: Default::default(),
698 buffers_being_formatted: Default::default(),
699 buffers_needing_diff: Default::default(),
700 git_diff_debouncer: DebouncedDelay::new(),
701 buffer_snapshots: Default::default(),
702 nonce: StdRng::from_entropy().gen(),
703 terminals: Terminals {
704 local_handles: Vec::new(),
705 },
706 copilot_lsp_subscription,
707 copilot_log_subscription: None,
708 current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
709 node: None,
710 default_prettier: DefaultPrettier::default(),
711 prettiers_per_worktree: HashMap::default(),
712 prettier_instances: HashMap::default(),
713 tasks,
714 };
715 this.set_role(role, cx);
716 for worktree in worktrees {
717 let _ = this.add_worktree(&worktree, cx);
718 }
719 this
720 })?;
721 let subscription = subscription.set_model(&this, &mut cx);
722
723 let user_ids = response
724 .payload
725 .collaborators
726 .iter()
727 .map(|peer| peer.user_id)
728 .collect();
729 user_store
730 .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
731 .await?;
732
733 this.update(&mut cx, |this, cx| {
734 this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
735 this.client_subscriptions.push(subscription);
736 anyhow::Ok(())
737 })??;
738
739 Ok(this)
740 }
741
742 fn release(&mut self, cx: &mut AppContext) {
743 match &self.client_state {
744 ProjectClientState::Local => {}
745 ProjectClientState::Shared { .. } => {
746 let _ = self.unshare_internal(cx);
747 }
748 ProjectClientState::Remote { remote_id, .. } => {
749 let _ = self.client.send(proto::LeaveProject {
750 project_id: *remote_id,
751 });
752 self.disconnected_from_host_internal(cx);
753 }
754 }
755 }
756
757 fn shutdown_language_servers(
758 &mut self,
759 _cx: &mut ModelContext<Self>,
760 ) -> impl Future<Output = ()> {
761 let shutdown_futures = self
762 .language_servers
763 .drain()
764 .map(|(_, server_state)| async {
765 use LanguageServerState::*;
766 match server_state {
767 Running { server, .. } => server.shutdown()?.await,
768 Starting(task) => task.await?.shutdown()?.await,
769 }
770 })
771 .collect::<Vec<_>>();
772
773 async move {
774 futures::future::join_all(shutdown_futures).await;
775 }
776 }
777
778 #[cfg(any(test, feature = "test-support"))]
779 pub async fn test(
780 fs: Arc<dyn Fs>,
781 root_paths: impl IntoIterator<Item = &Path>,
782 cx: &mut gpui::TestAppContext,
783 ) -> Model<Project> {
784 use clock::FakeSystemClock;
785
786 let mut languages = LanguageRegistry::test();
787 languages.set_executor(cx.executor());
788 let clock = Arc::new(FakeSystemClock::default());
789 let http_client = util::http::FakeHttpClient::with_404_response();
790 let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
791 let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
792 let project = cx.update(|cx| {
793 Project::local(
794 client,
795 node_runtime::FakeNodeRuntime::new(),
796 user_store,
797 Arc::new(languages),
798 fs,
799 cx,
800 )
801 });
802 for path in root_paths {
803 let (tree, _) = project
804 .update(cx, |project, cx| {
805 project.find_or_create_local_worktree(path, true, cx)
806 })
807 .await
808 .unwrap();
809 tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
810 .await;
811 }
812 project
813 }
814
815 fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
816 let mut language_servers_to_start = Vec::new();
817 let mut language_formatters_to_check = Vec::new();
818 for buffer in self.opened_buffers.values() {
819 if let Some(buffer) = buffer.upgrade() {
820 let buffer = buffer.read(cx);
821 let buffer_file = File::from_dyn(buffer.file());
822 let buffer_language = buffer.language();
823 let settings = language_settings(buffer_language, buffer.file(), cx);
824 if let Some(language) = buffer_language {
825 if settings.enable_language_server {
826 if let Some(file) = buffer_file {
827 language_servers_to_start
828 .push((file.worktree.clone(), Arc::clone(language)));
829 }
830 }
831 language_formatters_to_check.push((
832 buffer_file.map(|f| f.worktree_id(cx)),
833 Arc::clone(language),
834 settings.clone(),
835 ));
836 }
837 }
838 }
839
840 let mut language_servers_to_stop = Vec::new();
841 let mut language_servers_to_restart = Vec::new();
842 let languages = self.languages.to_vec();
843
844 let new_lsp_settings = ProjectSettings::get_global(cx).lsp.clone();
845 let current_lsp_settings = &self.current_lsp_settings;
846 for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
847 let language = languages.iter().find_map(|l| {
848 let adapter = l
849 .lsp_adapters()
850 .iter()
851 .find(|adapter| &adapter.name == started_lsp_name)?;
852 Some((l, adapter))
853 });
854 if let Some((language, adapter)) = language {
855 let worktree = self.worktree_for_id(*worktree_id, cx);
856 let file = worktree.as_ref().and_then(|tree| {
857 tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _))
858 });
859 if !language_settings(Some(language), file.as_ref(), cx).enable_language_server {
860 language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
861 } else if let Some(worktree) = worktree {
862 let server_name = &adapter.name.0;
863 match (
864 current_lsp_settings.get(server_name),
865 new_lsp_settings.get(server_name),
866 ) {
867 (None, None) => {}
868 (Some(_), None) | (None, Some(_)) => {
869 language_servers_to_restart.push((worktree, Arc::clone(language)));
870 }
871 (Some(current_lsp_settings), Some(new_lsp_settings)) => {
872 if current_lsp_settings != new_lsp_settings {
873 language_servers_to_restart.push((worktree, Arc::clone(language)));
874 }
875 }
876 }
877 }
878 }
879 }
880 self.current_lsp_settings = new_lsp_settings;
881
882 // Stop all newly-disabled language servers.
883 for (worktree_id, adapter_name) in language_servers_to_stop {
884 self.stop_language_server(worktree_id, adapter_name, cx)
885 .detach();
886 }
887
888 let mut prettier_plugins_by_worktree = HashMap::default();
889 for (worktree, language, settings) in language_formatters_to_check {
890 if let Some(plugins) =
891 prettier_support::prettier_plugins_for_language(&language, &settings)
892 {
893 prettier_plugins_by_worktree
894 .entry(worktree)
895 .or_insert_with(|| HashSet::default())
896 .extend(plugins);
897 }
898 }
899 for (worktree, prettier_plugins) in prettier_plugins_by_worktree {
900 self.install_default_prettier(worktree, prettier_plugins, cx);
901 }
902
903 // Start all the newly-enabled language servers.
904 for (worktree, language) in language_servers_to_start {
905 self.start_language_servers(&worktree, language, cx);
906 }
907
908 // Restart all language servers with changed initialization options.
909 for (worktree, language) in language_servers_to_restart {
910 self.restart_language_servers(worktree, language, cx);
911 }
912
913 if self.copilot_lsp_subscription.is_none() {
914 if let Some(copilot) = Copilot::global(cx) {
915 for buffer in self.opened_buffers.values() {
916 if let Some(buffer) = buffer.upgrade() {
917 self.register_buffer_with_copilot(&buffer, cx);
918 }
919 }
920 self.copilot_lsp_subscription = Some(subscribe_for_copilot_events(&copilot, cx));
921 }
922 }
923
924 cx.notify();
925 }
926
927 pub fn buffer_for_id(&self, remote_id: BufferId) -> Option<Model<Buffer>> {
928 self.opened_buffers
929 .get(&remote_id)
930 .and_then(|buffer| buffer.upgrade())
931 }
932
933 pub fn languages(&self) -> &Arc<LanguageRegistry> {
934 &self.languages
935 }
936
937 pub fn client(&self) -> Arc<Client> {
938 self.client.clone()
939 }
940
941 pub fn user_store(&self) -> Model<UserStore> {
942 self.user_store.clone()
943 }
944
945 pub fn opened_buffers(&self) -> Vec<Model<Buffer>> {
946 self.opened_buffers
947 .values()
948 .filter_map(|b| b.upgrade())
949 .collect()
950 }
951
952 #[cfg(any(test, feature = "test-support"))]
953 pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
954 let path = path.into();
955 if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
956 self.opened_buffers.iter().any(|(_, buffer)| {
957 if let Some(buffer) = buffer.upgrade() {
958 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
959 if file.worktree == worktree && file.path() == &path.path {
960 return true;
961 }
962 }
963 }
964 false
965 })
966 } else {
967 false
968 }
969 }
970
971 pub fn fs(&self) -> &Arc<dyn Fs> {
972 &self.fs
973 }
974
975 pub fn remote_id(&self) -> Option<u64> {
976 match self.client_state {
977 ProjectClientState::Local => None,
978 ProjectClientState::Shared { remote_id, .. }
979 | ProjectClientState::Remote { remote_id, .. } => Some(remote_id),
980 }
981 }
982
983 pub fn replica_id(&self) -> ReplicaId {
984 match self.client_state {
985 ProjectClientState::Remote { replica_id, .. } => replica_id,
986 _ => 0,
987 }
988 }
989
990 fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
991 if let ProjectClientState::Shared { updates_tx, .. } = &mut self.client_state {
992 updates_tx
993 .unbounded_send(LocalProjectUpdate::WorktreesChanged)
994 .ok();
995 }
996 cx.notify();
997 }
998
999 pub fn task_inventory(&self) -> &Model<Inventory> {
1000 &self.tasks
1001 }
1002
1003 pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
1004 &self.collaborators
1005 }
1006
1007 pub fn host(&self) -> Option<&Collaborator> {
1008 self.collaborators.values().find(|c| c.replica_id == 0)
1009 }
1010
1011 /// Collect all worktrees, including ones that don't appear in the project panel
1012 pub fn worktrees<'a>(&'a self) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1013 self.worktrees
1014 .iter()
1015 .filter_map(move |worktree| worktree.upgrade())
1016 }
1017
1018 /// Collect all user-visible worktrees, the ones that appear in the project panel
1019 pub fn visible_worktrees<'a>(
1020 &'a self,
1021 cx: &'a AppContext,
1022 ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1023 self.worktrees.iter().filter_map(|worktree| {
1024 worktree.upgrade().and_then(|worktree| {
1025 if worktree.read(cx).is_visible() {
1026 Some(worktree)
1027 } else {
1028 None
1029 }
1030 })
1031 })
1032 }
1033
1034 pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
1035 self.visible_worktrees(cx)
1036 .map(|tree| tree.read(cx).root_name())
1037 }
1038
1039 pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
1040 self.worktrees()
1041 .find(|worktree| worktree.read(cx).id() == id)
1042 }
1043
1044 pub fn worktree_for_entry(
1045 &self,
1046 entry_id: ProjectEntryId,
1047 cx: &AppContext,
1048 ) -> Option<Model<Worktree>> {
1049 self.worktrees()
1050 .find(|worktree| worktree.read(cx).contains_entry(entry_id))
1051 }
1052
1053 pub fn worktree_id_for_entry(
1054 &self,
1055 entry_id: ProjectEntryId,
1056 cx: &AppContext,
1057 ) -> Option<WorktreeId> {
1058 self.worktree_for_entry(entry_id, cx)
1059 .map(|worktree| worktree.read(cx).id())
1060 }
1061
1062 pub fn contains_paths(&self, paths: &[PathBuf], cx: &AppContext) -> bool {
1063 paths.iter().all(|path| self.contains_path(path, cx))
1064 }
1065
1066 pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool {
1067 for worktree in self.worktrees() {
1068 let worktree = worktree.read(cx).as_local();
1069 if worktree.map_or(false, |w| w.contains_abs_path(path)) {
1070 return true;
1071 }
1072 }
1073 false
1074 }
1075
1076 pub fn create_entry(
1077 &mut self,
1078 project_path: impl Into<ProjectPath>,
1079 is_directory: bool,
1080 cx: &mut ModelContext<Self>,
1081 ) -> Task<Result<Option<Entry>>> {
1082 let project_path = project_path.into();
1083 let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
1084 return Task::ready(Ok(None));
1085 };
1086 if self.is_local() {
1087 worktree.update(cx, |worktree, cx| {
1088 worktree
1089 .as_local_mut()
1090 .unwrap()
1091 .create_entry(project_path.path, is_directory, cx)
1092 })
1093 } else {
1094 let client = self.client.clone();
1095 let project_id = self.remote_id().unwrap();
1096 cx.spawn(move |_, mut cx| async move {
1097 let response = client
1098 .request(proto::CreateProjectEntry {
1099 worktree_id: project_path.worktree_id.to_proto(),
1100 project_id,
1101 path: project_path.path.to_string_lossy().into(),
1102 is_directory,
1103 })
1104 .await?;
1105 match response.entry {
1106 Some(entry) => worktree
1107 .update(&mut cx, |worktree, cx| {
1108 worktree.as_remote_mut().unwrap().insert_entry(
1109 entry,
1110 response.worktree_scan_id as usize,
1111 cx,
1112 )
1113 })?
1114 .await
1115 .map(Some),
1116 None => Ok(None),
1117 }
1118 })
1119 }
1120 }
1121
1122 pub fn copy_entry(
1123 &mut self,
1124 entry_id: ProjectEntryId,
1125 new_path: impl Into<Arc<Path>>,
1126 cx: &mut ModelContext<Self>,
1127 ) -> Task<Result<Option<Entry>>> {
1128 let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1129 return Task::ready(Ok(None));
1130 };
1131 let new_path = new_path.into();
1132 if self.is_local() {
1133 worktree.update(cx, |worktree, cx| {
1134 worktree
1135 .as_local_mut()
1136 .unwrap()
1137 .copy_entry(entry_id, new_path, cx)
1138 })
1139 } else {
1140 let client = self.client.clone();
1141 let project_id = self.remote_id().unwrap();
1142
1143 cx.spawn(move |_, mut cx| async move {
1144 let response = client
1145 .request(proto::CopyProjectEntry {
1146 project_id,
1147 entry_id: entry_id.to_proto(),
1148 new_path: new_path.to_string_lossy().into(),
1149 })
1150 .await?;
1151 match response.entry {
1152 Some(entry) => worktree
1153 .update(&mut cx, |worktree, cx| {
1154 worktree.as_remote_mut().unwrap().insert_entry(
1155 entry,
1156 response.worktree_scan_id as usize,
1157 cx,
1158 )
1159 })?
1160 .await
1161 .map(Some),
1162 None => Ok(None),
1163 }
1164 })
1165 }
1166 }
1167
1168 pub fn rename_entry(
1169 &mut self,
1170 entry_id: ProjectEntryId,
1171 new_path: impl Into<Arc<Path>>,
1172 cx: &mut ModelContext<Self>,
1173 ) -> Task<Result<Option<Entry>>> {
1174 let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1175 return Task::ready(Ok(None));
1176 };
1177 let new_path = new_path.into();
1178 if self.is_local() {
1179 worktree.update(cx, |worktree, cx| {
1180 worktree
1181 .as_local_mut()
1182 .unwrap()
1183 .rename_entry(entry_id, new_path, cx)
1184 })
1185 } else {
1186 let client = self.client.clone();
1187 let project_id = self.remote_id().unwrap();
1188
1189 cx.spawn(move |_, mut cx| async move {
1190 let response = client
1191 .request(proto::RenameProjectEntry {
1192 project_id,
1193 entry_id: entry_id.to_proto(),
1194 new_path: new_path.to_string_lossy().into(),
1195 })
1196 .await?;
1197 match response.entry {
1198 Some(entry) => worktree
1199 .update(&mut cx, |worktree, cx| {
1200 worktree.as_remote_mut().unwrap().insert_entry(
1201 entry,
1202 response.worktree_scan_id as usize,
1203 cx,
1204 )
1205 })?
1206 .await
1207 .map(Some),
1208 None => Ok(None),
1209 }
1210 })
1211 }
1212 }
1213
1214 pub fn delete_entry(
1215 &mut self,
1216 entry_id: ProjectEntryId,
1217 cx: &mut ModelContext<Self>,
1218 ) -> Option<Task<Result<()>>> {
1219 let worktree = self.worktree_for_entry(entry_id, cx)?;
1220
1221 cx.emit(Event::DeletedEntry(entry_id));
1222
1223 if self.is_local() {
1224 worktree.update(cx, |worktree, cx| {
1225 worktree.as_local_mut().unwrap().delete_entry(entry_id, cx)
1226 })
1227 } else {
1228 let client = self.client.clone();
1229 let project_id = self.remote_id().unwrap();
1230 Some(cx.spawn(move |_, mut cx| async move {
1231 let response = client
1232 .request(proto::DeleteProjectEntry {
1233 project_id,
1234 entry_id: entry_id.to_proto(),
1235 })
1236 .await?;
1237 worktree
1238 .update(&mut cx, move |worktree, cx| {
1239 worktree.as_remote_mut().unwrap().delete_entry(
1240 entry_id,
1241 response.worktree_scan_id as usize,
1242 cx,
1243 )
1244 })?
1245 .await
1246 }))
1247 }
1248 }
1249
1250 pub fn expand_entry(
1251 &mut self,
1252 worktree_id: WorktreeId,
1253 entry_id: ProjectEntryId,
1254 cx: &mut ModelContext<Self>,
1255 ) -> Option<Task<Result<()>>> {
1256 let worktree = self.worktree_for_id(worktree_id, cx)?;
1257 if self.is_local() {
1258 worktree.update(cx, |worktree, cx| {
1259 worktree.as_local_mut().unwrap().expand_entry(entry_id, cx)
1260 })
1261 } else {
1262 let worktree = worktree.downgrade();
1263 let request = self.client.request(proto::ExpandProjectEntry {
1264 project_id: self.remote_id().unwrap(),
1265 entry_id: entry_id.to_proto(),
1266 });
1267 Some(cx.spawn(move |_, mut cx| async move {
1268 let response = request.await?;
1269 if let Some(worktree) = worktree.upgrade() {
1270 worktree
1271 .update(&mut cx, |worktree, _| {
1272 worktree
1273 .as_remote_mut()
1274 .unwrap()
1275 .wait_for_snapshot(response.worktree_scan_id as usize)
1276 })?
1277 .await?;
1278 }
1279 Ok(())
1280 }))
1281 }
1282 }
1283
1284 pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
1285 if !matches!(self.client_state, ProjectClientState::Local) {
1286 return Err(anyhow!("project was already shared"));
1287 }
1288 self.client_subscriptions.push(
1289 self.client
1290 .subscribe_to_entity(project_id)?
1291 .set_model(&cx.handle(), &mut cx.to_async()),
1292 );
1293
1294 for open_buffer in self.opened_buffers.values_mut() {
1295 match open_buffer {
1296 OpenBuffer::Strong(_) => {}
1297 OpenBuffer::Weak(buffer) => {
1298 if let Some(buffer) = buffer.upgrade() {
1299 *open_buffer = OpenBuffer::Strong(buffer);
1300 }
1301 }
1302 OpenBuffer::Operations(_) => unreachable!(),
1303 }
1304 }
1305
1306 for worktree_handle in self.worktrees.iter_mut() {
1307 match worktree_handle {
1308 WorktreeHandle::Strong(_) => {}
1309 WorktreeHandle::Weak(worktree) => {
1310 if let Some(worktree) = worktree.upgrade() {
1311 *worktree_handle = WorktreeHandle::Strong(worktree);
1312 }
1313 }
1314 }
1315 }
1316
1317 for (server_id, status) in &self.language_server_statuses {
1318 self.client
1319 .send(proto::StartLanguageServer {
1320 project_id,
1321 server: Some(proto::LanguageServer {
1322 id: server_id.0 as u64,
1323 name: status.name.clone(),
1324 }),
1325 })
1326 .log_err();
1327 }
1328
1329 let store = cx.global::<SettingsStore>();
1330 for worktree in self.worktrees() {
1331 let worktree_id = worktree.read(cx).id().to_proto();
1332 for (path, content) in store.local_settings(worktree.entity_id().as_u64() as usize) {
1333 self.client
1334 .send(proto::UpdateWorktreeSettings {
1335 project_id,
1336 worktree_id,
1337 path: path.to_string_lossy().into(),
1338 content: Some(content),
1339 })
1340 .log_err();
1341 }
1342 }
1343
1344 let (updates_tx, mut updates_rx) = mpsc::unbounded();
1345 let client = self.client.clone();
1346 self.client_state = ProjectClientState::Shared {
1347 remote_id: project_id,
1348 updates_tx,
1349 _send_updates: cx.spawn(move |this, mut cx| async move {
1350 while let Some(update) = updates_rx.next().await {
1351 match update {
1352 LocalProjectUpdate::WorktreesChanged => {
1353 let worktrees = this.update(&mut cx, |this, _cx| {
1354 this.worktrees().collect::<Vec<_>>()
1355 })?;
1356 let update_project = this
1357 .update(&mut cx, |this, cx| {
1358 this.client.request(proto::UpdateProject {
1359 project_id,
1360 worktrees: this.worktree_metadata_protos(cx),
1361 })
1362 })?
1363 .await;
1364 if update_project.is_ok() {
1365 for worktree in worktrees {
1366 worktree.update(&mut cx, |worktree, cx| {
1367 let worktree = worktree.as_local_mut().unwrap();
1368 worktree.share(project_id, cx).detach_and_log_err(cx)
1369 })?;
1370 }
1371 }
1372 }
1373 LocalProjectUpdate::CreateBufferForPeer { peer_id, buffer_id } => {
1374 let buffer = this.update(&mut cx, |this, _| {
1375 let buffer = this.opened_buffers.get(&buffer_id).unwrap();
1376 let shared_buffers =
1377 this.shared_buffers.entry(peer_id).or_default();
1378 if shared_buffers.insert(buffer_id) {
1379 if let OpenBuffer::Strong(buffer) = buffer {
1380 Some(buffer.clone())
1381 } else {
1382 None
1383 }
1384 } else {
1385 None
1386 }
1387 })?;
1388
1389 let Some(buffer) = buffer else { continue };
1390 let operations =
1391 buffer.update(&mut cx, |b, cx| b.serialize_ops(None, cx))?;
1392 let operations = operations.await;
1393 let state = buffer.update(&mut cx, |buffer, _| buffer.to_proto())?;
1394
1395 let initial_state = proto::CreateBufferForPeer {
1396 project_id,
1397 peer_id: Some(peer_id),
1398 variant: Some(proto::create_buffer_for_peer::Variant::State(state)),
1399 };
1400 if client.send(initial_state).log_err().is_some() {
1401 let client = client.clone();
1402 cx.background_executor()
1403 .spawn(async move {
1404 let mut chunks = split_operations(operations).peekable();
1405 while let Some(chunk) = chunks.next() {
1406 let is_last = chunks.peek().is_none();
1407 client.send(proto::CreateBufferForPeer {
1408 project_id,
1409 peer_id: Some(peer_id),
1410 variant: Some(
1411 proto::create_buffer_for_peer::Variant::Chunk(
1412 proto::BufferChunk {
1413 buffer_id: buffer_id.into(),
1414 operations: chunk,
1415 is_last,
1416 },
1417 ),
1418 ),
1419 })?;
1420 }
1421 anyhow::Ok(())
1422 })
1423 .await
1424 .log_err();
1425 }
1426 }
1427 }
1428 }
1429 Ok(())
1430 }),
1431 };
1432
1433 self.metadata_changed(cx);
1434 cx.emit(Event::RemoteIdChanged(Some(project_id)));
1435 cx.notify();
1436 Ok(())
1437 }
1438
1439 pub fn reshared(
1440 &mut self,
1441 message: proto::ResharedProject,
1442 cx: &mut ModelContext<Self>,
1443 ) -> Result<()> {
1444 self.shared_buffers.clear();
1445 self.set_collaborators_from_proto(message.collaborators, cx)?;
1446 self.metadata_changed(cx);
1447 Ok(())
1448 }
1449
1450 pub fn rejoined(
1451 &mut self,
1452 message: proto::RejoinedProject,
1453 message_id: u32,
1454 cx: &mut ModelContext<Self>,
1455 ) -> Result<()> {
1456 cx.update_global::<SettingsStore, _>(|store, cx| {
1457 for worktree in &self.worktrees {
1458 store
1459 .clear_local_settings(worktree.handle_id(), cx)
1460 .log_err();
1461 }
1462 });
1463
1464 self.join_project_response_message_id = message_id;
1465 self.set_worktrees_from_proto(message.worktrees, cx)?;
1466 self.set_collaborators_from_proto(message.collaborators, cx)?;
1467 self.language_server_statuses = message
1468 .language_servers
1469 .into_iter()
1470 .map(|server| {
1471 (
1472 LanguageServerId(server.id as usize),
1473 LanguageServerStatus {
1474 name: server.name,
1475 pending_work: Default::default(),
1476 has_pending_diagnostic_updates: false,
1477 progress_tokens: Default::default(),
1478 },
1479 )
1480 })
1481 .collect();
1482 self.buffer_ordered_messages_tx
1483 .unbounded_send(BufferOrderedMessage::Resync)
1484 .unwrap();
1485 cx.notify();
1486 Ok(())
1487 }
1488
1489 pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
1490 self.unshare_internal(cx)?;
1491 self.metadata_changed(cx);
1492 cx.notify();
1493 Ok(())
1494 }
1495
1496 fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
1497 if self.is_remote() {
1498 return Err(anyhow!("attempted to unshare a remote project"));
1499 }
1500
1501 if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
1502 self.client_state = ProjectClientState::Local;
1503 self.collaborators.clear();
1504 self.shared_buffers.clear();
1505 self.client_subscriptions.clear();
1506
1507 for worktree_handle in self.worktrees.iter_mut() {
1508 if let WorktreeHandle::Strong(worktree) = worktree_handle {
1509 let is_visible = worktree.update(cx, |worktree, _| {
1510 worktree.as_local_mut().unwrap().unshare();
1511 worktree.is_visible()
1512 });
1513 if !is_visible {
1514 *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
1515 }
1516 }
1517 }
1518
1519 for open_buffer in self.opened_buffers.values_mut() {
1520 // Wake up any tasks waiting for peers' edits to this buffer.
1521 if let Some(buffer) = open_buffer.upgrade() {
1522 buffer.update(cx, |buffer, _| buffer.give_up_waiting());
1523 }
1524
1525 if let OpenBuffer::Strong(buffer) = open_buffer {
1526 *open_buffer = OpenBuffer::Weak(buffer.downgrade());
1527 }
1528 }
1529
1530 self.client.send(proto::UnshareProject {
1531 project_id: remote_id,
1532 })?;
1533
1534 Ok(())
1535 } else {
1536 Err(anyhow!("attempted to unshare an unshared project"))
1537 }
1538 }
1539
1540 pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
1541 self.disconnected_from_host_internal(cx);
1542 cx.emit(Event::DisconnectedFromHost);
1543 cx.notify();
1544 }
1545
1546 pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut ModelContext<Self>) {
1547 let new_capability =
1548 if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
1549 Capability::ReadWrite
1550 } else {
1551 Capability::ReadOnly
1552 };
1553 if let ProjectClientState::Remote { capability, .. } = &mut self.client_state {
1554 if *capability == new_capability {
1555 return;
1556 }
1557
1558 *capability = new_capability;
1559 for buffer in self.opened_buffers() {
1560 buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
1561 }
1562 }
1563 }
1564
1565 fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
1566 if let ProjectClientState::Remote {
1567 sharing_has_stopped,
1568 ..
1569 } = &mut self.client_state
1570 {
1571 *sharing_has_stopped = true;
1572
1573 self.collaborators.clear();
1574
1575 for worktree in &self.worktrees {
1576 if let Some(worktree) = worktree.upgrade() {
1577 worktree.update(cx, |worktree, _| {
1578 if let Some(worktree) = worktree.as_remote_mut() {
1579 worktree.disconnected_from_host();
1580 }
1581 });
1582 }
1583 }
1584
1585 for open_buffer in self.opened_buffers.values_mut() {
1586 // Wake up any tasks waiting for peers' edits to this buffer.
1587 if let Some(buffer) = open_buffer.upgrade() {
1588 buffer.update(cx, |buffer, _| buffer.give_up_waiting());
1589 }
1590
1591 if let OpenBuffer::Strong(buffer) = open_buffer {
1592 *open_buffer = OpenBuffer::Weak(buffer.downgrade());
1593 }
1594 }
1595
1596 // Wake up all futures currently waiting on a buffer to get opened,
1597 // to give them a chance to fail now that we've disconnected.
1598 *self.opened_buffer.0.borrow_mut() = ();
1599 }
1600 }
1601
1602 pub fn close(&mut self, cx: &mut ModelContext<Self>) {
1603 cx.emit(Event::Closed);
1604 }
1605
1606 pub fn is_disconnected(&self) -> bool {
1607 match &self.client_state {
1608 ProjectClientState::Remote {
1609 sharing_has_stopped,
1610 ..
1611 } => *sharing_has_stopped,
1612 _ => false,
1613 }
1614 }
1615
1616 pub fn capability(&self) -> Capability {
1617 match &self.client_state {
1618 ProjectClientState::Remote { capability, .. } => *capability,
1619 ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
1620 }
1621 }
1622
1623 pub fn is_read_only(&self) -> bool {
1624 self.is_disconnected() || self.capability() == Capability::ReadOnly
1625 }
1626
1627 pub fn is_local(&self) -> bool {
1628 match &self.client_state {
1629 ProjectClientState::Local | ProjectClientState::Shared { .. } => true,
1630 ProjectClientState::Remote { .. } => false,
1631 }
1632 }
1633
1634 pub fn is_remote(&self) -> bool {
1635 !self.is_local()
1636 }
1637
1638 pub fn create_buffer(
1639 &mut self,
1640 text: &str,
1641 language: Option<Arc<Language>>,
1642 cx: &mut ModelContext<Self>,
1643 ) -> Result<Model<Buffer>> {
1644 if self.is_remote() {
1645 return Err(anyhow!("creating buffers as a guest is not supported yet"));
1646 }
1647 let id = self.next_buffer_id.next();
1648 let buffer = cx.new_model(|cx| {
1649 Buffer::new(self.replica_id(), id, text)
1650 .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
1651 });
1652 self.register_buffer(&buffer, cx)?;
1653 Ok(buffer)
1654 }
1655
1656 pub fn open_path(
1657 &mut self,
1658 path: ProjectPath,
1659 cx: &mut ModelContext<Self>,
1660 ) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
1661 let task = self.open_buffer(path.clone(), cx);
1662 cx.spawn(move |_, cx| async move {
1663 let buffer = task.await?;
1664 let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
1665 File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1666 })?;
1667
1668 let buffer: &AnyModel = &buffer;
1669 Ok((project_entry_id, buffer.clone()))
1670 })
1671 }
1672
1673 pub fn open_local_buffer(
1674 &mut self,
1675 abs_path: impl AsRef<Path>,
1676 cx: &mut ModelContext<Self>,
1677 ) -> Task<Result<Model<Buffer>>> {
1678 if let Some((worktree, relative_path)) = self.find_local_worktree(abs_path.as_ref(), cx) {
1679 self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1680 } else {
1681 Task::ready(Err(anyhow!("no such path")))
1682 }
1683 }
1684
1685 pub fn open_buffer(
1686 &mut self,
1687 path: impl Into<ProjectPath>,
1688 cx: &mut ModelContext<Self>,
1689 ) -> Task<Result<Model<Buffer>>> {
1690 let project_path = path.into();
1691 let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
1692 worktree
1693 } else {
1694 return Task::ready(Err(anyhow!("no such worktree")));
1695 };
1696
1697 // If there is already a buffer for the given path, then return it.
1698 let existing_buffer = self.get_open_buffer(&project_path, cx);
1699 if let Some(existing_buffer) = existing_buffer {
1700 return Task::ready(Ok(existing_buffer));
1701 }
1702
1703 let loading_watch = match self.loading_buffers_by_path.entry(project_path.clone()) {
1704 // If the given path is already being loaded, then wait for that existing
1705 // task to complete and return the same buffer.
1706 hash_map::Entry::Occupied(e) => e.get().clone(),
1707
1708 // Otherwise, record the fact that this path is now being loaded.
1709 hash_map::Entry::Vacant(entry) => {
1710 let (mut tx, rx) = postage::watch::channel();
1711 entry.insert(rx.clone());
1712
1713 let load_buffer = if worktree.read(cx).is_local() {
1714 self.open_local_buffer_internal(&project_path.path, &worktree, cx)
1715 } else {
1716 self.open_remote_buffer_internal(&project_path.path, &worktree, cx)
1717 };
1718
1719 let project_path = project_path.clone();
1720 cx.spawn(move |this, mut cx| async move {
1721 let load_result = load_buffer.await;
1722 *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
1723 // Record the fact that the buffer is no longer loading.
1724 this.loading_buffers_by_path.remove(&project_path);
1725 let buffer = load_result.map_err(Arc::new)?;
1726 Ok(buffer)
1727 })?);
1728 anyhow::Ok(())
1729 })
1730 .detach();
1731 rx
1732 }
1733 };
1734
1735 cx.background_executor().spawn(async move {
1736 wait_for_loading_buffer(loading_watch)
1737 .await
1738 .map_err(|e| e.cloned())
1739 })
1740 }
1741
1742 fn open_local_buffer_internal(
1743 &mut self,
1744 path: &Arc<Path>,
1745 worktree: &Model<Worktree>,
1746 cx: &mut ModelContext<Self>,
1747 ) -> Task<Result<Model<Buffer>>> {
1748 let buffer_id = self.next_buffer_id.next();
1749 let load_buffer = worktree.update(cx, |worktree, cx| {
1750 let worktree = worktree.as_local_mut().unwrap();
1751 worktree.load_buffer(buffer_id, path, cx)
1752 });
1753 cx.spawn(move |this, mut cx| async move {
1754 let buffer = load_buffer.await?;
1755 this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))??;
1756 Ok(buffer)
1757 })
1758 }
1759
1760 fn open_remote_buffer_internal(
1761 &mut self,
1762 path: &Arc<Path>,
1763 worktree: &Model<Worktree>,
1764 cx: &mut ModelContext<Self>,
1765 ) -> Task<Result<Model<Buffer>>> {
1766 let rpc = self.client.clone();
1767 let project_id = self.remote_id().unwrap();
1768 let remote_worktree_id = worktree.read(cx).id();
1769 let path = path.clone();
1770 let path_string = path.to_string_lossy().to_string();
1771 cx.spawn(move |this, mut cx| async move {
1772 let response = rpc
1773 .request(proto::OpenBufferByPath {
1774 project_id,
1775 worktree_id: remote_worktree_id.to_proto(),
1776 path: path_string,
1777 })
1778 .await?;
1779 let buffer_id = BufferId::new(response.buffer_id)?;
1780 this.update(&mut cx, |this, cx| {
1781 this.wait_for_remote_buffer(buffer_id, cx)
1782 })?
1783 .await
1784 })
1785 }
1786
1787 /// LanguageServerName is owned, because it is inserted into a map
1788 pub fn open_local_buffer_via_lsp(
1789 &mut self,
1790 abs_path: lsp::Url,
1791 language_server_id: LanguageServerId,
1792 language_server_name: LanguageServerName,
1793 cx: &mut ModelContext<Self>,
1794 ) -> Task<Result<Model<Buffer>>> {
1795 cx.spawn(move |this, mut cx| async move {
1796 let abs_path = abs_path
1797 .to_file_path()
1798 .map_err(|_| anyhow!("can't convert URI to path"))?;
1799 let (worktree, relative_path) = if let Some(result) =
1800 this.update(&mut cx, |this, cx| this.find_local_worktree(&abs_path, cx))?
1801 {
1802 result
1803 } else {
1804 let worktree = this
1805 .update(&mut cx, |this, cx| {
1806 this.create_local_worktree(&abs_path, false, cx)
1807 })?
1808 .await?;
1809 this.update(&mut cx, |this, cx| {
1810 this.language_server_ids.insert(
1811 (worktree.read(cx).id(), language_server_name),
1812 language_server_id,
1813 );
1814 })
1815 .ok();
1816 (worktree, PathBuf::new())
1817 };
1818
1819 let project_path = ProjectPath {
1820 worktree_id: worktree.update(&mut cx, |worktree, _| worktree.id())?,
1821 path: relative_path.into(),
1822 };
1823 this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))?
1824 .await
1825 })
1826 }
1827
1828 pub fn open_buffer_by_id(
1829 &mut self,
1830 id: BufferId,
1831 cx: &mut ModelContext<Self>,
1832 ) -> Task<Result<Model<Buffer>>> {
1833 if let Some(buffer) = self.buffer_for_id(id) {
1834 Task::ready(Ok(buffer))
1835 } else if self.is_local() {
1836 Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1837 } else if let Some(project_id) = self.remote_id() {
1838 let request = self.client.request(proto::OpenBufferById {
1839 project_id,
1840 id: id.into(),
1841 });
1842 cx.spawn(move |this, mut cx| async move {
1843 let buffer_id = BufferId::new(request.await?.buffer_id)?;
1844 this.update(&mut cx, |this, cx| {
1845 this.wait_for_remote_buffer(buffer_id, cx)
1846 })?
1847 .await
1848 })
1849 } else {
1850 Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1851 }
1852 }
1853
1854 pub fn save_buffers(
1855 &self,
1856 buffers: HashSet<Model<Buffer>>,
1857 cx: &mut ModelContext<Self>,
1858 ) -> Task<Result<()>> {
1859 cx.spawn(move |this, mut cx| async move {
1860 let save_tasks = buffers.into_iter().filter_map(|buffer| {
1861 this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx))
1862 .ok()
1863 });
1864 try_join_all(save_tasks).await?;
1865 Ok(())
1866 })
1867 }
1868
1869 pub fn save_buffer(
1870 &self,
1871 buffer: Model<Buffer>,
1872 cx: &mut ModelContext<Self>,
1873 ) -> Task<Result<()>> {
1874 let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
1875 return Task::ready(Err(anyhow!("buffer doesn't have a file")));
1876 };
1877 let worktree = file.worktree.clone();
1878 let path = file.path.clone();
1879 worktree.update(cx, |worktree, cx| match worktree {
1880 Worktree::Local(worktree) => worktree.save_buffer(buffer, path, false, cx),
1881 Worktree::Remote(worktree) => worktree.save_buffer(buffer, cx),
1882 })
1883 }
1884
1885 pub fn save_buffer_as(
1886 &mut self,
1887 buffer: Model<Buffer>,
1888 abs_path: PathBuf,
1889 cx: &mut ModelContext<Self>,
1890 ) -> Task<Result<()>> {
1891 let worktree_task = self.find_or_create_local_worktree(&abs_path, true, cx);
1892 let old_file = File::from_dyn(buffer.read(cx).file())
1893 .filter(|f| f.is_local())
1894 .cloned();
1895 cx.spawn(move |this, mut cx| async move {
1896 if let Some(old_file) = &old_file {
1897 this.update(&mut cx, |this, cx| {
1898 this.unregister_buffer_from_language_servers(&buffer, old_file, cx);
1899 })?;
1900 }
1901 let (worktree, path) = worktree_task.await?;
1902 worktree
1903 .update(&mut cx, |worktree, cx| match worktree {
1904 Worktree::Local(worktree) => {
1905 worktree.save_buffer(buffer.clone(), path.into(), true, cx)
1906 }
1907 Worktree::Remote(_) => panic!("cannot remote buffers as new files"),
1908 })?
1909 .await?;
1910
1911 this.update(&mut cx, |this, cx| {
1912 this.detect_language_for_buffer(&buffer, cx);
1913 this.register_buffer_with_language_servers(&buffer, cx);
1914 })?;
1915 Ok(())
1916 })
1917 }
1918
1919 pub fn get_open_buffer(
1920 &mut self,
1921 path: &ProjectPath,
1922 cx: &mut ModelContext<Self>,
1923 ) -> Option<Model<Buffer>> {
1924 let worktree = self.worktree_for_id(path.worktree_id, cx)?;
1925 self.opened_buffers.values().find_map(|buffer| {
1926 let buffer = buffer.upgrade()?;
1927 let file = File::from_dyn(buffer.read(cx).file())?;
1928 if file.worktree == worktree && file.path() == &path.path {
1929 Some(buffer)
1930 } else {
1931 None
1932 }
1933 })
1934 }
1935
1936 fn register_buffer(
1937 &mut self,
1938 buffer: &Model<Buffer>,
1939 cx: &mut ModelContext<Self>,
1940 ) -> Result<()> {
1941 self.request_buffer_diff_recalculation(buffer, cx);
1942 buffer.update(cx, |buffer, _| {
1943 buffer.set_language_registry(self.languages.clone())
1944 });
1945
1946 let remote_id = buffer.read(cx).remote_id();
1947 let is_remote = self.is_remote();
1948 let open_buffer = if is_remote || self.is_shared() {
1949 OpenBuffer::Strong(buffer.clone())
1950 } else {
1951 OpenBuffer::Weak(buffer.downgrade())
1952 };
1953
1954 match self.opened_buffers.entry(remote_id) {
1955 hash_map::Entry::Vacant(entry) => {
1956 entry.insert(open_buffer);
1957 }
1958 hash_map::Entry::Occupied(mut entry) => {
1959 if let OpenBuffer::Operations(operations) = entry.get_mut() {
1960 buffer.update(cx, |b, cx| b.apply_ops(operations.drain(..), cx))?;
1961 } else if entry.get().upgrade().is_some() {
1962 if is_remote {
1963 return Ok(());
1964 } else {
1965 debug_panic!("buffer {} was already registered", remote_id);
1966 Err(anyhow!("buffer {} was already registered", remote_id))?;
1967 }
1968 }
1969 entry.insert(open_buffer);
1970 }
1971 }
1972 cx.subscribe(buffer, |this, buffer, event, cx| {
1973 this.on_buffer_event(buffer, event, cx);
1974 })
1975 .detach();
1976
1977 if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
1978 if file.is_local {
1979 self.local_buffer_ids_by_path.insert(
1980 ProjectPath {
1981 worktree_id: file.worktree_id(cx),
1982 path: file.path.clone(),
1983 },
1984 remote_id,
1985 );
1986
1987 if let Some(entry_id) = file.entry_id {
1988 self.local_buffer_ids_by_entry_id
1989 .insert(entry_id, remote_id);
1990 }
1991 }
1992 }
1993
1994 self.detect_language_for_buffer(buffer, cx);
1995 self.register_buffer_with_language_servers(buffer, cx);
1996 self.register_buffer_with_copilot(buffer, cx);
1997 cx.observe_release(buffer, |this, buffer, cx| {
1998 if let Some(file) = File::from_dyn(buffer.file()) {
1999 if file.is_local() {
2000 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
2001 for server in this.language_servers_for_buffer(buffer, cx) {
2002 server
2003 .1
2004 .notify::<lsp::notification::DidCloseTextDocument>(
2005 lsp::DidCloseTextDocumentParams {
2006 text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
2007 },
2008 )
2009 .log_err();
2010 }
2011 }
2012 }
2013 })
2014 .detach();
2015
2016 *self.opened_buffer.0.borrow_mut() = ();
2017 Ok(())
2018 }
2019
2020 fn register_buffer_with_language_servers(
2021 &mut self,
2022 buffer_handle: &Model<Buffer>,
2023 cx: &mut ModelContext<Self>,
2024 ) {
2025 let buffer = buffer_handle.read(cx);
2026 let buffer_id = buffer.remote_id();
2027
2028 if let Some(file) = File::from_dyn(buffer.file()) {
2029 if !file.is_local() {
2030 return;
2031 }
2032
2033 let abs_path = file.abs_path(cx);
2034 let uri = lsp::Url::from_file_path(&abs_path)
2035 .unwrap_or_else(|()| panic!("Failed to register file {abs_path:?}"));
2036 let initial_snapshot = buffer.text_snapshot();
2037 let language = buffer.language().cloned();
2038 let worktree_id = file.worktree_id(cx);
2039
2040 if let Some(local_worktree) = file.worktree.read(cx).as_local() {
2041 for (server_id, diagnostics) in local_worktree.diagnostics_for_path(file.path()) {
2042 self.update_buffer_diagnostics(buffer_handle, server_id, None, diagnostics, cx)
2043 .log_err();
2044 }
2045 }
2046
2047 if let Some(language) = language {
2048 for adapter in language.lsp_adapters() {
2049 let language_id = adapter.language_ids.get(language.name().as_ref()).cloned();
2050 let server = self
2051 .language_server_ids
2052 .get(&(worktree_id, adapter.name.clone()))
2053 .and_then(|id| self.language_servers.get(id))
2054 .and_then(|server_state| {
2055 if let LanguageServerState::Running { server, .. } = server_state {
2056 Some(server.clone())
2057 } else {
2058 None
2059 }
2060 });
2061 let server = match server {
2062 Some(server) => server,
2063 None => continue,
2064 };
2065
2066 server
2067 .notify::<lsp::notification::DidOpenTextDocument>(
2068 lsp::DidOpenTextDocumentParams {
2069 text_document: lsp::TextDocumentItem::new(
2070 uri.clone(),
2071 language_id.unwrap_or_default(),
2072 0,
2073 initial_snapshot.text(),
2074 ),
2075 },
2076 )
2077 .log_err();
2078
2079 buffer_handle.update(cx, |buffer, cx| {
2080 buffer.set_completion_triggers(
2081 server
2082 .capabilities()
2083 .completion_provider
2084 .as_ref()
2085 .and_then(|provider| provider.trigger_characters.clone())
2086 .unwrap_or_default(),
2087 cx,
2088 );
2089 });
2090
2091 let snapshot = LspBufferSnapshot {
2092 version: 0,
2093 snapshot: initial_snapshot.clone(),
2094 };
2095 self.buffer_snapshots
2096 .entry(buffer_id)
2097 .or_default()
2098 .insert(server.server_id(), vec![snapshot]);
2099 }
2100 }
2101 }
2102 }
2103
2104 fn unregister_buffer_from_language_servers(
2105 &mut self,
2106 buffer: &Model<Buffer>,
2107 old_file: &File,
2108 cx: &mut ModelContext<Self>,
2109 ) {
2110 let old_path = match old_file.as_local() {
2111 Some(local) => local.abs_path(cx),
2112 None => return,
2113 };
2114
2115 buffer.update(cx, |buffer, cx| {
2116 let worktree_id = old_file.worktree_id(cx);
2117 let ids = &self.language_server_ids;
2118
2119 let language = buffer.language().cloned();
2120 let adapters = language.iter().flat_map(|language| language.lsp_adapters());
2121 for &server_id in adapters.flat_map(|a| ids.get(&(worktree_id, a.name.clone()))) {
2122 buffer.update_diagnostics(server_id, Default::default(), cx);
2123 }
2124
2125 self.buffer_snapshots.remove(&buffer.remote_id());
2126 let file_url = lsp::Url::from_file_path(old_path).unwrap();
2127 for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
2128 language_server
2129 .notify::<lsp::notification::DidCloseTextDocument>(
2130 lsp::DidCloseTextDocumentParams {
2131 text_document: lsp::TextDocumentIdentifier::new(file_url.clone()),
2132 },
2133 )
2134 .log_err();
2135 }
2136 });
2137 }
2138
2139 fn register_buffer_with_copilot(
2140 &self,
2141 buffer_handle: &Model<Buffer>,
2142 cx: &mut ModelContext<Self>,
2143 ) {
2144 if let Some(copilot) = Copilot::global(cx) {
2145 copilot.update(cx, |copilot, cx| copilot.register_buffer(buffer_handle, cx));
2146 }
2147 }
2148
2149 async fn send_buffer_ordered_messages(
2150 this: WeakModel<Self>,
2151 rx: UnboundedReceiver<BufferOrderedMessage>,
2152 mut cx: AsyncAppContext,
2153 ) -> Result<()> {
2154 const MAX_BATCH_SIZE: usize = 128;
2155
2156 let mut operations_by_buffer_id = HashMap::default();
2157 async fn flush_operations(
2158 this: &WeakModel<Project>,
2159 operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
2160 needs_resync_with_host: &mut bool,
2161 is_local: bool,
2162 cx: &mut AsyncAppContext,
2163 ) -> Result<()> {
2164 for (buffer_id, operations) in operations_by_buffer_id.drain() {
2165 let request = this.update(cx, |this, _| {
2166 let project_id = this.remote_id()?;
2167 Some(this.client.request(proto::UpdateBuffer {
2168 buffer_id: buffer_id.into(),
2169 project_id,
2170 operations,
2171 }))
2172 })?;
2173 if let Some(request) = request {
2174 if request.await.is_err() && !is_local {
2175 *needs_resync_with_host = true;
2176 break;
2177 }
2178 }
2179 }
2180 Ok(())
2181 }
2182
2183 let mut needs_resync_with_host = false;
2184 let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
2185
2186 while let Some(changes) = changes.next().await {
2187 let is_local = this.update(&mut cx, |this, _| this.is_local())?;
2188
2189 for change in changes {
2190 match change {
2191 BufferOrderedMessage::Operation {
2192 buffer_id,
2193 operation,
2194 } => {
2195 if needs_resync_with_host {
2196 continue;
2197 }
2198
2199 operations_by_buffer_id
2200 .entry(buffer_id)
2201 .or_insert(Vec::new())
2202 .push(operation);
2203 }
2204
2205 BufferOrderedMessage::Resync => {
2206 operations_by_buffer_id.clear();
2207 if this
2208 .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))?
2209 .await
2210 .is_ok()
2211 {
2212 needs_resync_with_host = false;
2213 }
2214 }
2215
2216 BufferOrderedMessage::LanguageServerUpdate {
2217 language_server_id,
2218 message,
2219 } => {
2220 flush_operations(
2221 &this,
2222 &mut operations_by_buffer_id,
2223 &mut needs_resync_with_host,
2224 is_local,
2225 &mut cx,
2226 )
2227 .await?;
2228
2229 this.update(&mut cx, |this, _| {
2230 if let Some(project_id) = this.remote_id() {
2231 this.client
2232 .send(proto::UpdateLanguageServer {
2233 project_id,
2234 language_server_id: language_server_id.0 as u64,
2235 variant: Some(message),
2236 })
2237 .log_err();
2238 }
2239 })?;
2240 }
2241 }
2242 }
2243
2244 flush_operations(
2245 &this,
2246 &mut operations_by_buffer_id,
2247 &mut needs_resync_with_host,
2248 is_local,
2249 &mut cx,
2250 )
2251 .await?;
2252 }
2253
2254 Ok(())
2255 }
2256
2257 fn on_buffer_event(
2258 &mut self,
2259 buffer: Model<Buffer>,
2260 event: &BufferEvent,
2261 cx: &mut ModelContext<Self>,
2262 ) -> Option<()> {
2263 if matches!(
2264 event,
2265 BufferEvent::Edited { .. } | BufferEvent::Reloaded | BufferEvent::DiffBaseChanged
2266 ) {
2267 self.request_buffer_diff_recalculation(&buffer, cx);
2268 }
2269
2270 match event {
2271 BufferEvent::Operation(operation) => {
2272 self.buffer_ordered_messages_tx
2273 .unbounded_send(BufferOrderedMessage::Operation {
2274 buffer_id: buffer.read(cx).remote_id(),
2275 operation: language::proto::serialize_operation(operation),
2276 })
2277 .ok();
2278 }
2279
2280 BufferEvent::Edited { .. } => {
2281 let buffer = buffer.read(cx);
2282 let file = File::from_dyn(buffer.file())?;
2283 let abs_path = file.as_local()?.abs_path(cx);
2284 let uri = lsp::Url::from_file_path(abs_path).unwrap();
2285 let next_snapshot = buffer.text_snapshot();
2286
2287 let language_servers: Vec<_> = self
2288 .language_servers_for_buffer(buffer, cx)
2289 .map(|i| i.1.clone())
2290 .collect();
2291
2292 for language_server in language_servers {
2293 let language_server = language_server.clone();
2294
2295 let buffer_snapshots = self
2296 .buffer_snapshots
2297 .get_mut(&buffer.remote_id())
2298 .and_then(|m| m.get_mut(&language_server.server_id()))?;
2299 let previous_snapshot = buffer_snapshots.last()?;
2300
2301 let build_incremental_change = || {
2302 buffer
2303 .edits_since::<(PointUtf16, usize)>(
2304 previous_snapshot.snapshot.version(),
2305 )
2306 .map(|edit| {
2307 let edit_start = edit.new.start.0;
2308 let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
2309 let new_text = next_snapshot
2310 .text_for_range(edit.new.start.1..edit.new.end.1)
2311 .collect();
2312 lsp::TextDocumentContentChangeEvent {
2313 range: Some(lsp::Range::new(
2314 point_to_lsp(edit_start),
2315 point_to_lsp(edit_end),
2316 )),
2317 range_length: None,
2318 text: new_text,
2319 }
2320 })
2321 .collect()
2322 };
2323
2324 let document_sync_kind = language_server
2325 .capabilities()
2326 .text_document_sync
2327 .as_ref()
2328 .and_then(|sync| match sync {
2329 lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
2330 lsp::TextDocumentSyncCapability::Options(options) => options.change,
2331 });
2332
2333 let content_changes: Vec<_> = match document_sync_kind {
2334 Some(lsp::TextDocumentSyncKind::FULL) => {
2335 vec![lsp::TextDocumentContentChangeEvent {
2336 range: None,
2337 range_length: None,
2338 text: next_snapshot.text(),
2339 }]
2340 }
2341 Some(lsp::TextDocumentSyncKind::INCREMENTAL) => build_incremental_change(),
2342 _ => {
2343 #[cfg(any(test, feature = "test-support"))]
2344 {
2345 build_incremental_change()
2346 }
2347
2348 #[cfg(not(any(test, feature = "test-support")))]
2349 {
2350 continue;
2351 }
2352 }
2353 };
2354
2355 let next_version = previous_snapshot.version + 1;
2356
2357 buffer_snapshots.push(LspBufferSnapshot {
2358 version: next_version,
2359 snapshot: next_snapshot.clone(),
2360 });
2361
2362 language_server
2363 .notify::<lsp::notification::DidChangeTextDocument>(
2364 lsp::DidChangeTextDocumentParams {
2365 text_document: lsp::VersionedTextDocumentIdentifier::new(
2366 uri.clone(),
2367 next_version,
2368 ),
2369 content_changes,
2370 },
2371 )
2372 .log_err();
2373 }
2374 }
2375
2376 BufferEvent::Saved => {
2377 let file = File::from_dyn(buffer.read(cx).file())?;
2378 let worktree_id = file.worktree_id(cx);
2379 let abs_path = file.as_local()?.abs_path(cx);
2380 let text_document = lsp::TextDocumentIdentifier {
2381 uri: lsp::Url::from_file_path(abs_path).unwrap(),
2382 };
2383
2384 for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
2385 let text = include_text(server.as_ref()).then(|| buffer.read(cx).text());
2386
2387 server
2388 .notify::<lsp::notification::DidSaveTextDocument>(
2389 lsp::DidSaveTextDocumentParams {
2390 text_document: text_document.clone(),
2391 text,
2392 },
2393 )
2394 .log_err();
2395 }
2396
2397 let language_server_ids = self.language_server_ids_for_buffer(buffer.read(cx), cx);
2398 for language_server_id in language_server_ids {
2399 if let Some(LanguageServerState::Running {
2400 adapter,
2401 simulate_disk_based_diagnostics_completion,
2402 ..
2403 }) = self.language_servers.get_mut(&language_server_id)
2404 {
2405 // After saving a buffer using a language server that doesn't provide
2406 // a disk-based progress token, kick off a timer that will reset every
2407 // time the buffer is saved. If the timer eventually fires, simulate
2408 // disk-based diagnostics being finished so that other pieces of UI
2409 // (e.g., project diagnostics view, diagnostic status bar) can update.
2410 // We don't emit an event right away because the language server might take
2411 // some time to publish diagnostics.
2412 if adapter.disk_based_diagnostics_progress_token.is_none() {
2413 const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration =
2414 Duration::from_secs(1);
2415
2416 let task = cx.spawn(move |this, mut cx| async move {
2417 cx.background_executor().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
2418 if let Some(this) = this.upgrade() {
2419 this.update(&mut cx, |this, cx| {
2420 this.disk_based_diagnostics_finished(
2421 language_server_id,
2422 cx,
2423 );
2424 this.buffer_ordered_messages_tx
2425 .unbounded_send(
2426 BufferOrderedMessage::LanguageServerUpdate {
2427 language_server_id,
2428 message:proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(Default::default())
2429 },
2430 )
2431 .ok();
2432 }).ok();
2433 }
2434 });
2435 *simulate_disk_based_diagnostics_completion = Some(task);
2436 }
2437 }
2438 }
2439 }
2440 BufferEvent::FileHandleChanged => {
2441 let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
2442 return None;
2443 };
2444
2445 let remote_id = buffer.read(cx).remote_id();
2446 if let Some(entry_id) = file.entry_id {
2447 match self.local_buffer_ids_by_entry_id.get(&entry_id) {
2448 Some(_) => {
2449 return None;
2450 }
2451 None => {
2452 self.local_buffer_ids_by_entry_id
2453 .insert(entry_id, remote_id);
2454 }
2455 }
2456 };
2457 self.local_buffer_ids_by_path.insert(
2458 ProjectPath {
2459 worktree_id: file.worktree_id(cx),
2460 path: file.path.clone(),
2461 },
2462 remote_id,
2463 );
2464 }
2465 _ => {}
2466 }
2467
2468 None
2469 }
2470
2471 fn request_buffer_diff_recalculation(
2472 &mut self,
2473 buffer: &Model<Buffer>,
2474 cx: &mut ModelContext<Self>,
2475 ) {
2476 self.buffers_needing_diff.insert(buffer.downgrade());
2477 let first_insertion = self.buffers_needing_diff.len() == 1;
2478
2479 let settings = ProjectSettings::get_global(cx);
2480 let delay = if let Some(delay) = settings.git.gutter_debounce {
2481 delay
2482 } else {
2483 if first_insertion {
2484 let this = cx.weak_model();
2485 cx.defer(move |cx| {
2486 if let Some(this) = this.upgrade() {
2487 this.update(cx, |this, cx| {
2488 this.recalculate_buffer_diffs(cx).detach();
2489 });
2490 }
2491 });
2492 }
2493 return;
2494 };
2495
2496 const MIN_DELAY: u64 = 50;
2497 let delay = delay.max(MIN_DELAY);
2498 let duration = Duration::from_millis(delay);
2499
2500 self.git_diff_debouncer
2501 .fire_new(duration, cx, move |this, cx| {
2502 this.recalculate_buffer_diffs(cx)
2503 });
2504 }
2505
2506 fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
2507 let buffers = self.buffers_needing_diff.drain().collect::<Vec<_>>();
2508 cx.spawn(move |this, mut cx| async move {
2509 let tasks: Vec<_> = buffers
2510 .iter()
2511 .filter_map(|buffer| {
2512 let buffer = buffer.upgrade()?;
2513 buffer
2514 .update(&mut cx, |buffer, cx| buffer.git_diff_recalc(cx))
2515 .ok()
2516 .flatten()
2517 })
2518 .collect();
2519
2520 futures::future::join_all(tasks).await;
2521
2522 this.update(&mut cx, |this, cx| {
2523 if !this.buffers_needing_diff.is_empty() {
2524 this.recalculate_buffer_diffs(cx).detach();
2525 } else {
2526 // TODO: Would a `ModelContext<Project>.notify()` suffice here?
2527 for buffer in buffers {
2528 if let Some(buffer) = buffer.upgrade() {
2529 buffer.update(cx, |_, cx| cx.notify());
2530 }
2531 }
2532 }
2533 })
2534 .ok();
2535 })
2536 }
2537
2538 fn language_servers_for_worktree(
2539 &self,
2540 worktree_id: WorktreeId,
2541 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<Language>, &Arc<LanguageServer>)> {
2542 self.language_server_ids
2543 .iter()
2544 .filter_map(move |((language_server_worktree_id, _), id)| {
2545 if *language_server_worktree_id == worktree_id {
2546 if let Some(LanguageServerState::Running {
2547 adapter,
2548 language,
2549 server,
2550 ..
2551 }) = self.language_servers.get(id)
2552 {
2553 return Some((adapter, language, server));
2554 }
2555 }
2556 None
2557 })
2558 }
2559
2560 fn maintain_buffer_languages(
2561 languages: Arc<LanguageRegistry>,
2562 cx: &mut ModelContext<Project>,
2563 ) -> Task<()> {
2564 let mut subscription = languages.subscribe();
2565 let mut prev_reload_count = languages.reload_count();
2566 cx.spawn(move |project, mut cx| async move {
2567 while let Some(()) = subscription.next().await {
2568 if let Some(project) = project.upgrade() {
2569 // If the language registry has been reloaded, then remove and
2570 // re-assign the languages on all open buffers.
2571 let reload_count = languages.reload_count();
2572 if reload_count > prev_reload_count {
2573 prev_reload_count = reload_count;
2574 project
2575 .update(&mut cx, |this, cx| {
2576 let buffers = this
2577 .opened_buffers
2578 .values()
2579 .filter_map(|b| b.upgrade())
2580 .collect::<Vec<_>>();
2581 for buffer in buffers {
2582 if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned()
2583 {
2584 this.unregister_buffer_from_language_servers(
2585 &buffer, &f, cx,
2586 );
2587 buffer
2588 .update(cx, |buffer, cx| buffer.set_language(None, cx));
2589 }
2590 }
2591 })
2592 .ok();
2593 }
2594
2595 project
2596 .update(&mut cx, |project, cx| {
2597 let mut plain_text_buffers = Vec::new();
2598 let mut buffers_with_unknown_injections = Vec::new();
2599 for buffer in project.opened_buffers.values() {
2600 if let Some(handle) = buffer.upgrade() {
2601 let buffer = &handle.read(cx);
2602 if buffer.language().is_none()
2603 || buffer.language() == Some(&*language::PLAIN_TEXT)
2604 {
2605 plain_text_buffers.push(handle);
2606 } else if buffer.contains_unknown_injections() {
2607 buffers_with_unknown_injections.push(handle);
2608 }
2609 }
2610 }
2611
2612 for buffer in plain_text_buffers {
2613 project.detect_language_for_buffer(&buffer, cx);
2614 project.register_buffer_with_language_servers(&buffer, cx);
2615 }
2616
2617 for buffer in buffers_with_unknown_injections {
2618 buffer.update(cx, |buffer, cx| buffer.reparse(cx));
2619 }
2620 })
2621 .ok();
2622 }
2623 }
2624 })
2625 }
2626
2627 fn maintain_workspace_config(cx: &mut ModelContext<Project>) -> Task<Result<()>> {
2628 let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
2629 let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
2630
2631 let settings_observation = cx.observe_global::<SettingsStore>(move |_, _| {
2632 *settings_changed_tx.borrow_mut() = ();
2633 });
2634
2635 cx.spawn(move |this, mut cx| async move {
2636 while let Some(()) = settings_changed_rx.next().await {
2637 let servers: Vec<_> = this.update(&mut cx, |this, _| {
2638 this.language_servers
2639 .values()
2640 .filter_map(|state| match state {
2641 LanguageServerState::Starting(_) => None,
2642 LanguageServerState::Running {
2643 adapter, server, ..
2644 } => Some((adapter.clone(), server.clone())),
2645 })
2646 .collect()
2647 })?;
2648
2649 for (adapter, server) in servers {
2650 let settings =
2651 cx.update(|cx| adapter.workspace_configuration(server.root_path(), cx))?;
2652
2653 server
2654 .notify::<lsp::notification::DidChangeConfiguration>(
2655 lsp::DidChangeConfigurationParams { settings },
2656 )
2657 .ok();
2658 }
2659 }
2660
2661 drop(settings_observation);
2662 anyhow::Ok(())
2663 })
2664 }
2665
2666 fn detect_language_for_buffer(
2667 &mut self,
2668 buffer_handle: &Model<Buffer>,
2669 cx: &mut ModelContext<Self>,
2670 ) -> Option<()> {
2671 // If the buffer has a language, set it and start the language server if we haven't already.
2672 let buffer = buffer_handle.read(cx);
2673 let full_path = buffer.file()?.full_path(cx);
2674 let content = buffer.as_rope();
2675 let new_language = self
2676 .languages
2677 .language_for_file(&full_path, Some(content))
2678 .now_or_never()?
2679 .ok()?;
2680 self.set_language_for_buffer(buffer_handle, new_language, cx);
2681 None
2682 }
2683
2684 pub fn set_language_for_buffer(
2685 &mut self,
2686 buffer: &Model<Buffer>,
2687 new_language: Arc<Language>,
2688 cx: &mut ModelContext<Self>,
2689 ) {
2690 buffer.update(cx, |buffer, cx| {
2691 if buffer.language().map_or(true, |old_language| {
2692 !Arc::ptr_eq(old_language, &new_language)
2693 }) {
2694 buffer.set_language(Some(new_language.clone()), cx);
2695 }
2696 });
2697
2698 let buffer_file = buffer.read(cx).file().cloned();
2699 let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
2700 let buffer_file = File::from_dyn(buffer_file.as_ref());
2701 let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
2702 if let Some(prettier_plugins) =
2703 prettier_support::prettier_plugins_for_language(&new_language, &settings)
2704 {
2705 self.install_default_prettier(worktree, prettier_plugins, cx);
2706 };
2707 if let Some(file) = buffer_file {
2708 let worktree = file.worktree.clone();
2709 if worktree.read(cx).is_local() {
2710 self.start_language_servers(&worktree, new_language, cx);
2711 }
2712 }
2713 }
2714
2715 fn start_language_servers(
2716 &mut self,
2717 worktree: &Model<Worktree>,
2718 language: Arc<Language>,
2719 cx: &mut ModelContext<Self>,
2720 ) {
2721 let root_file = worktree.update(cx, |tree, cx| tree.root_file(cx));
2722 let settings = language_settings(Some(&language), root_file.map(|f| f as _).as_ref(), cx);
2723 if !settings.enable_language_server {
2724 return;
2725 }
2726
2727 for adapter in language.lsp_adapters() {
2728 self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
2729 }
2730 }
2731
2732 fn start_language_server(
2733 &mut self,
2734 worktree_handle: &Model<Worktree>,
2735 adapter: Arc<CachedLspAdapter>,
2736 language: Arc<Language>,
2737 cx: &mut ModelContext<Self>,
2738 ) {
2739 if adapter.reinstall_attempt_count.load(SeqCst) > MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
2740 return;
2741 }
2742
2743 let worktree = worktree_handle.read(cx);
2744 let worktree_id = worktree.id();
2745 let worktree_path = worktree.abs_path();
2746 let key = (worktree_id, adapter.name.clone());
2747 if self.language_server_ids.contains_key(&key) {
2748 return;
2749 }
2750
2751 let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
2752 let pending_server = match self.languages.create_pending_language_server(
2753 stderr_capture.clone(),
2754 language.clone(),
2755 adapter.clone(),
2756 Arc::clone(&worktree_path),
2757 ProjectLspAdapterDelegate::new(self, worktree_handle, cx),
2758 cx,
2759 ) {
2760 Some(pending_server) => pending_server,
2761 None => return,
2762 };
2763
2764 let project_settings =
2765 ProjectSettings::get(Some((worktree_id.to_proto() as usize, Path::new(""))), cx);
2766 let lsp = project_settings.lsp.get(&adapter.name.0);
2767 let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
2768
2769 let server_id = pending_server.server_id;
2770 let container_dir = pending_server.container_dir.clone();
2771 let state = LanguageServerState::Starting({
2772 let adapter = adapter.clone();
2773 let server_name = adapter.name.0.clone();
2774 let language = language.clone();
2775 let key = key.clone();
2776
2777 cx.spawn(move |this, mut cx| async move {
2778 let result = Self::setup_and_insert_language_server(
2779 this.clone(),
2780 &worktree_path,
2781 override_options,
2782 pending_server,
2783 adapter.clone(),
2784 language.clone(),
2785 server_id,
2786 key,
2787 &mut cx,
2788 )
2789 .await;
2790
2791 match result {
2792 Ok(server) => {
2793 stderr_capture.lock().take();
2794 server
2795 }
2796
2797 Err(err) => {
2798 log::error!("failed to start language server {server_name:?}: {err}");
2799 log::error!("server stderr: {:?}", stderr_capture.lock().take());
2800
2801 let this = this.upgrade()?;
2802 let container_dir = container_dir?;
2803
2804 let attempt_count = adapter.reinstall_attempt_count.fetch_add(1, SeqCst);
2805 if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
2806 let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
2807 log::error!("Hit {max} reinstallation attempts for {server_name:?}");
2808 return None;
2809 }
2810
2811 log::info!(
2812 "retrying installation of language server {server_name:?} in {}s",
2813 SERVER_REINSTALL_DEBOUNCE_TIMEOUT.as_secs()
2814 );
2815 cx.background_executor()
2816 .timer(SERVER_REINSTALL_DEBOUNCE_TIMEOUT)
2817 .await;
2818
2819 let installation_test_binary = adapter
2820 .installation_test_binary(container_dir.to_path_buf())
2821 .await;
2822
2823 this.update(&mut cx, |_, cx| {
2824 Self::check_errored_server(
2825 language,
2826 adapter,
2827 server_id,
2828 installation_test_binary,
2829 cx,
2830 )
2831 })
2832 .ok();
2833
2834 None
2835 }
2836 }
2837 })
2838 });
2839
2840 self.language_servers.insert(server_id, state);
2841 self.language_server_ids.insert(key, server_id);
2842 }
2843
2844 fn reinstall_language_server(
2845 &mut self,
2846 language: Arc<Language>,
2847 adapter: Arc<CachedLspAdapter>,
2848 server_id: LanguageServerId,
2849 cx: &mut ModelContext<Self>,
2850 ) -> Option<Task<()>> {
2851 log::info!("beginning to reinstall server");
2852
2853 let existing_server = match self.language_servers.remove(&server_id) {
2854 Some(LanguageServerState::Running { server, .. }) => Some(server),
2855 _ => None,
2856 };
2857
2858 for worktree in &self.worktrees {
2859 if let Some(worktree) = worktree.upgrade() {
2860 let key = (worktree.read(cx).id(), adapter.name.clone());
2861 self.language_server_ids.remove(&key);
2862 }
2863 }
2864
2865 Some(cx.spawn(move |this, mut cx| async move {
2866 if let Some(task) = existing_server.and_then(|server| server.shutdown()) {
2867 log::info!("shutting down existing server");
2868 task.await;
2869 }
2870
2871 // TODO: This is race-safe with regards to preventing new instances from
2872 // starting while deleting, but existing instances in other projects are going
2873 // to be very confused and messed up
2874 let Some(task) = this
2875 .update(&mut cx, |this, cx| {
2876 this.languages.delete_server_container(adapter.clone(), cx)
2877 })
2878 .log_err()
2879 else {
2880 return;
2881 };
2882 task.await;
2883
2884 this.update(&mut cx, |this, cx| {
2885 let worktrees = this.worktrees.clone();
2886 for worktree in worktrees {
2887 if let Some(worktree) = worktree.upgrade() {
2888 this.start_language_server(
2889 &worktree,
2890 adapter.clone(),
2891 language.clone(),
2892 cx,
2893 );
2894 }
2895 }
2896 })
2897 .ok();
2898 }))
2899 }
2900
2901 async fn setup_and_insert_language_server(
2902 this: WeakModel<Self>,
2903 worktree_path: &Path,
2904 override_initialization_options: Option<serde_json::Value>,
2905 pending_server: PendingLanguageServer,
2906 adapter: Arc<CachedLspAdapter>,
2907 language: Arc<Language>,
2908 server_id: LanguageServerId,
2909 key: (WorktreeId, LanguageServerName),
2910 cx: &mut AsyncAppContext,
2911 ) -> Result<Option<Arc<LanguageServer>>> {
2912 let language_server = Self::setup_pending_language_server(
2913 this.clone(),
2914 override_initialization_options,
2915 pending_server,
2916 worktree_path,
2917 adapter.clone(),
2918 server_id,
2919 cx,
2920 )
2921 .await?;
2922
2923 let this = match this.upgrade() {
2924 Some(this) => this,
2925 None => return Err(anyhow!("failed to upgrade project handle")),
2926 };
2927
2928 this.update(cx, |this, cx| {
2929 this.insert_newly_running_language_server(
2930 language,
2931 adapter,
2932 language_server.clone(),
2933 server_id,
2934 key,
2935 cx,
2936 )
2937 })??;
2938
2939 Ok(Some(language_server))
2940 }
2941
2942 async fn setup_pending_language_server(
2943 this: WeakModel<Self>,
2944 override_options: Option<serde_json::Value>,
2945 pending_server: PendingLanguageServer,
2946 worktree_path: &Path,
2947 adapter: Arc<CachedLspAdapter>,
2948 server_id: LanguageServerId,
2949 cx: &mut AsyncAppContext,
2950 ) -> Result<Arc<LanguageServer>> {
2951 let workspace_config =
2952 cx.update(|cx| adapter.workspace_configuration(worktree_path, cx))?;
2953 let language_server = pending_server.task.await?;
2954
2955 let name = language_server.name();
2956 language_server
2957 .on_notification::<lsp::notification::PublishDiagnostics, _>({
2958 let adapter = adapter.clone();
2959 let this = this.clone();
2960 move |mut params, mut cx| {
2961 let adapter = adapter.clone();
2962 if let Some(this) = this.upgrade() {
2963 adapter.process_diagnostics(&mut params);
2964 this.update(&mut cx, |this, cx| {
2965 this.update_diagnostics(
2966 server_id,
2967 params,
2968 &adapter.disk_based_diagnostic_sources,
2969 cx,
2970 )
2971 .log_err();
2972 })
2973 .ok();
2974 }
2975 }
2976 })
2977 .detach();
2978
2979 language_server
2980 .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
2981 let adapter = adapter.clone();
2982 let worktree_path = worktree_path.to_path_buf();
2983 move |params, cx| {
2984 let adapter = adapter.clone();
2985 let worktree_path = worktree_path.clone();
2986 async move {
2987 let workspace_config =
2988 cx.update(|cx| adapter.workspace_configuration(&worktree_path, cx))?;
2989 Ok(params
2990 .items
2991 .into_iter()
2992 .map(|item| {
2993 if let Some(section) = &item.section {
2994 workspace_config
2995 .get(section)
2996 .cloned()
2997 .unwrap_or(serde_json::Value::Null)
2998 } else {
2999 workspace_config.clone()
3000 }
3001 })
3002 .collect())
3003 }
3004 }
3005 })
3006 .detach();
3007
3008 // Even though we don't have handling for these requests, respond to them to
3009 // avoid stalling any language server like `gopls` which waits for a response
3010 // to these requests when initializing.
3011 language_server
3012 .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
3013 let this = this.clone();
3014 move |params, mut cx| {
3015 let this = this.clone();
3016 async move {
3017 this.update(&mut cx, |this, _| {
3018 if let Some(status) = this.language_server_statuses.get_mut(&server_id)
3019 {
3020 if let lsp::NumberOrString::String(token) = params.token {
3021 status.progress_tokens.insert(token);
3022 }
3023 }
3024 })?;
3025
3026 Ok(())
3027 }
3028 }
3029 })
3030 .detach();
3031
3032 language_server
3033 .on_request::<lsp::request::RegisterCapability, _, _>({
3034 let this = this.clone();
3035 move |params, mut cx| {
3036 let this = this.clone();
3037 async move {
3038 for reg in params.registrations {
3039 if reg.method == "workspace/didChangeWatchedFiles" {
3040 if let Some(options) = reg.register_options {
3041 let options = serde_json::from_value(options)?;
3042 this.update(&mut cx, |this, cx| {
3043 this.on_lsp_did_change_watched_files(
3044 server_id, options, cx,
3045 );
3046 })?;
3047 }
3048 }
3049 }
3050 Ok(())
3051 }
3052 }
3053 })
3054 .detach();
3055
3056 language_server
3057 .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
3058 let adapter = adapter.clone();
3059 let this = this.clone();
3060 move |params, cx| {
3061 Self::on_lsp_workspace_edit(
3062 this.clone(),
3063 params,
3064 server_id,
3065 adapter.clone(),
3066 cx,
3067 )
3068 }
3069 })
3070 .detach();
3071
3072 language_server
3073 .on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
3074 let this = this.clone();
3075 move |(), mut cx| {
3076 let this = this.clone();
3077 async move {
3078 this.update(&mut cx, |project, cx| {
3079 cx.emit(Event::RefreshInlayHints);
3080 project.remote_id().map(|project_id| {
3081 project.client.send(proto::RefreshInlayHints { project_id })
3082 })
3083 })?
3084 .transpose()?;
3085 Ok(())
3086 }
3087 }
3088 })
3089 .detach();
3090
3091 language_server
3092 .on_request::<lsp::request::ShowMessageRequest, _, _>({
3093 let this = this.clone();
3094 let name = name.to_string();
3095 move |params, mut cx| {
3096 let this = this.clone();
3097 let name = name.to_string();
3098 async move {
3099 if let Some(actions) = params.actions {
3100 let (tx, mut rx) = smol::channel::bounded(1);
3101 let request = LanguageServerPromptRequest {
3102 level: match params.typ {
3103 lsp::MessageType::ERROR => PromptLevel::Critical,
3104 lsp::MessageType::WARNING => PromptLevel::Warning,
3105 _ => PromptLevel::Info,
3106 },
3107 message: params.message,
3108 actions,
3109 response_channel: tx,
3110 lsp_name: name.clone(),
3111 };
3112
3113 if let Ok(_) = this.update(&mut cx, |_, cx| {
3114 cx.emit(Event::LanguageServerPrompt(request));
3115 }) {
3116 let response = rx.next().await;
3117
3118 Ok(response)
3119 } else {
3120 Ok(None)
3121 }
3122 } else {
3123 Ok(None)
3124 }
3125 }
3126 }
3127 })
3128 .detach();
3129
3130 let disk_based_diagnostics_progress_token =
3131 adapter.disk_based_diagnostics_progress_token.clone();
3132
3133 language_server
3134 .on_notification::<lsp::notification::Progress, _>(move |params, mut cx| {
3135 if let Some(this) = this.upgrade() {
3136 this.update(&mut cx, |this, cx| {
3137 this.on_lsp_progress(
3138 params,
3139 server_id,
3140 disk_based_diagnostics_progress_token.clone(),
3141 cx,
3142 );
3143 })
3144 .ok();
3145 }
3146 })
3147 .detach();
3148
3149 let mut initialization_options = adapter.adapter.initialization_options();
3150 match (&mut initialization_options, override_options) {
3151 (Some(initialization_options), Some(override_options)) => {
3152 merge_json_value_into(override_options, initialization_options);
3153 }
3154 (None, override_options) => initialization_options = override_options,
3155 _ => {}
3156 }
3157 let language_server = cx
3158 .update(|cx| language_server.initialize(initialization_options, cx))?
3159 .await?;
3160
3161 language_server
3162 .notify::<lsp::notification::DidChangeConfiguration>(
3163 lsp::DidChangeConfigurationParams {
3164 settings: workspace_config,
3165 },
3166 )
3167 .ok();
3168
3169 Ok(language_server)
3170 }
3171
3172 fn insert_newly_running_language_server(
3173 &mut self,
3174 language: Arc<Language>,
3175 adapter: Arc<CachedLspAdapter>,
3176 language_server: Arc<LanguageServer>,
3177 server_id: LanguageServerId,
3178 key: (WorktreeId, LanguageServerName),
3179 cx: &mut ModelContext<Self>,
3180 ) -> Result<()> {
3181 // If the language server for this key doesn't match the server id, don't store the
3182 // server. Which will cause it to be dropped, killing the process
3183 if self
3184 .language_server_ids
3185 .get(&key)
3186 .map(|id| id != &server_id)
3187 .unwrap_or(false)
3188 {
3189 return Ok(());
3190 }
3191
3192 // Update language_servers collection with Running variant of LanguageServerState
3193 // indicating that the server is up and running and ready
3194 self.language_servers.insert(
3195 server_id,
3196 LanguageServerState::Running {
3197 adapter: adapter.clone(),
3198 language: language.clone(),
3199 watched_paths: Default::default(),
3200 server: language_server.clone(),
3201 simulate_disk_based_diagnostics_completion: None,
3202 },
3203 );
3204
3205 self.language_server_statuses.insert(
3206 server_id,
3207 LanguageServerStatus {
3208 name: language_server.name().to_string(),
3209 pending_work: Default::default(),
3210 has_pending_diagnostic_updates: false,
3211 progress_tokens: Default::default(),
3212 },
3213 );
3214
3215 cx.emit(Event::LanguageServerAdded(server_id));
3216
3217 if let Some(project_id) = self.remote_id() {
3218 self.client.send(proto::StartLanguageServer {
3219 project_id,
3220 server: Some(proto::LanguageServer {
3221 id: server_id.0 as u64,
3222 name: language_server.name().to_string(),
3223 }),
3224 })?;
3225 }
3226
3227 // Tell the language server about every open buffer in the worktree that matches the language.
3228 for buffer in self.opened_buffers.values() {
3229 if let Some(buffer_handle) = buffer.upgrade() {
3230 let buffer = buffer_handle.read(cx);
3231 let file = match File::from_dyn(buffer.file()) {
3232 Some(file) => file,
3233 None => continue,
3234 };
3235 let language = match buffer.language() {
3236 Some(language) => language,
3237 None => continue,
3238 };
3239
3240 if file.worktree.read(cx).id() != key.0
3241 || !language.lsp_adapters().iter().any(|a| a.name == key.1)
3242 {
3243 continue;
3244 }
3245
3246 let file = match file.as_local() {
3247 Some(file) => file,
3248 None => continue,
3249 };
3250
3251 let versions = self
3252 .buffer_snapshots
3253 .entry(buffer.remote_id())
3254 .or_default()
3255 .entry(server_id)
3256 .or_insert_with(|| {
3257 vec![LspBufferSnapshot {
3258 version: 0,
3259 snapshot: buffer.text_snapshot(),
3260 }]
3261 });
3262
3263 let snapshot = versions.last().unwrap();
3264 let version = snapshot.version;
3265 let initial_snapshot = &snapshot.snapshot;
3266 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
3267 language_server.notify::<lsp::notification::DidOpenTextDocument>(
3268 lsp::DidOpenTextDocumentParams {
3269 text_document: lsp::TextDocumentItem::new(
3270 uri,
3271 adapter
3272 .language_ids
3273 .get(language.name().as_ref())
3274 .cloned()
3275 .unwrap_or_default(),
3276 version,
3277 initial_snapshot.text(),
3278 ),
3279 },
3280 )?;
3281
3282 buffer_handle.update(cx, |buffer, cx| {
3283 buffer.set_completion_triggers(
3284 language_server
3285 .capabilities()
3286 .completion_provider
3287 .as_ref()
3288 .and_then(|provider| provider.trigger_characters.clone())
3289 .unwrap_or_default(),
3290 cx,
3291 )
3292 });
3293 }
3294 }
3295
3296 cx.notify();
3297 Ok(())
3298 }
3299
3300 // Returns a list of all of the worktrees which no longer have a language server and the root path
3301 // for the stopped server
3302 fn stop_language_server(
3303 &mut self,
3304 worktree_id: WorktreeId,
3305 adapter_name: LanguageServerName,
3306 cx: &mut ModelContext<Self>,
3307 ) -> Task<Vec<WorktreeId>> {
3308 let key = (worktree_id, adapter_name);
3309 if let Some(server_id) = self.language_server_ids.remove(&key) {
3310 let name = key.1 .0;
3311 log::info!("stopping language server {name}");
3312
3313 // Remove other entries for this language server as well
3314 let mut orphaned_worktrees = vec![worktree_id];
3315 let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
3316 for other_key in other_keys {
3317 if self.language_server_ids.get(&other_key) == Some(&server_id) {
3318 self.language_server_ids.remove(&other_key);
3319 orphaned_worktrees.push(other_key.0);
3320 }
3321 }
3322
3323 for buffer in self.opened_buffers.values() {
3324 if let Some(buffer) = buffer.upgrade() {
3325 buffer.update(cx, |buffer, cx| {
3326 buffer.update_diagnostics(server_id, Default::default(), cx);
3327 });
3328 }
3329 }
3330 for worktree in &self.worktrees {
3331 if let Some(worktree) = worktree.upgrade() {
3332 worktree.update(cx, |worktree, cx| {
3333 if let Some(worktree) = worktree.as_local_mut() {
3334 worktree.clear_diagnostics_for_language_server(server_id, cx);
3335 }
3336 });
3337 }
3338 }
3339
3340 self.language_server_statuses.remove(&server_id);
3341 cx.notify();
3342
3343 let server_state = self.language_servers.remove(&server_id);
3344 cx.emit(Event::LanguageServerRemoved(server_id));
3345 cx.spawn(move |this, cx| async move {
3346 Self::shutdown_language_server(this, server_state, name, server_id, cx).await;
3347 orphaned_worktrees
3348 })
3349 } else {
3350 Task::ready(Vec::new())
3351 }
3352 }
3353
3354 async fn shutdown_language_server(
3355 this: WeakModel<Project>,
3356 server_state: Option<LanguageServerState>,
3357 name: Arc<str>,
3358 server_id: LanguageServerId,
3359 mut cx: AsyncAppContext,
3360 ) {
3361 let server = match server_state {
3362 Some(LanguageServerState::Starting(task)) => {
3363 let mut timer = cx
3364 .background_executor()
3365 .timer(SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT)
3366 .fuse();
3367
3368 select! {
3369 server = task.fuse() => server,
3370 _ = timer => {
3371 log::info!(
3372 "timeout waiting for language server {} to finish launching before stopping",
3373 name
3374 );
3375 None
3376 },
3377 }
3378 }
3379
3380 Some(LanguageServerState::Running { server, .. }) => Some(server),
3381
3382 None => None,
3383 };
3384
3385 if let Some(server) = server {
3386 if let Some(shutdown) = server.shutdown() {
3387 shutdown.await;
3388 }
3389 }
3390
3391 if let Some(this) = this.upgrade() {
3392 this.update(&mut cx, |this, cx| {
3393 this.language_server_statuses.remove(&server_id);
3394 cx.notify();
3395 })
3396 .ok();
3397 }
3398 }
3399
3400 pub fn restart_language_servers_for_buffers(
3401 &mut self,
3402 buffers: impl IntoIterator<Item = Model<Buffer>>,
3403 cx: &mut ModelContext<Self>,
3404 ) -> Option<()> {
3405 let language_server_lookup_info: HashSet<(Model<Worktree>, Arc<Language>)> = buffers
3406 .into_iter()
3407 .filter_map(|buffer| {
3408 let buffer = buffer.read(cx);
3409 let file = File::from_dyn(buffer.file())?;
3410 let full_path = file.full_path(cx);
3411 let language = self
3412 .languages
3413 .language_for_file(&full_path, Some(buffer.as_rope()))
3414 .now_or_never()?
3415 .ok()?;
3416 Some((file.worktree.clone(), language))
3417 })
3418 .collect();
3419 for (worktree, language) in language_server_lookup_info {
3420 self.restart_language_servers(worktree, language, cx);
3421 }
3422
3423 None
3424 }
3425
3426 fn restart_language_servers(
3427 &mut self,
3428 worktree: Model<Worktree>,
3429 language: Arc<Language>,
3430 cx: &mut ModelContext<Self>,
3431 ) {
3432 let worktree_id = worktree.read(cx).id();
3433
3434 let stop_tasks = language
3435 .lsp_adapters()
3436 .iter()
3437 .map(|adapter| {
3438 let stop_task = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
3439 (stop_task, adapter.name.clone())
3440 })
3441 .collect::<Vec<_>>();
3442 if stop_tasks.is_empty() {
3443 return;
3444 }
3445
3446 cx.spawn(move |this, mut cx| async move {
3447 // For each stopped language server, record all of the worktrees with which
3448 // it was associated.
3449 let mut affected_worktrees = Vec::new();
3450 for (stop_task, language_server_name) in stop_tasks {
3451 for affected_worktree_id in stop_task.await {
3452 affected_worktrees.push((affected_worktree_id, language_server_name.clone()));
3453 }
3454 }
3455
3456 this.update(&mut cx, |this, cx| {
3457 // Restart the language server for the given worktree.
3458 this.start_language_servers(&worktree, language.clone(), cx);
3459
3460 // Lookup new server ids and set them for each of the orphaned worktrees
3461 for (affected_worktree_id, language_server_name) in affected_worktrees {
3462 if let Some(new_server_id) = this
3463 .language_server_ids
3464 .get(&(worktree_id, language_server_name.clone()))
3465 .cloned()
3466 {
3467 this.language_server_ids
3468 .insert((affected_worktree_id, language_server_name), new_server_id);
3469 }
3470 }
3471 })
3472 .ok();
3473 })
3474 .detach();
3475 }
3476
3477 fn check_errored_server(
3478 language: Arc<Language>,
3479 adapter: Arc<CachedLspAdapter>,
3480 server_id: LanguageServerId,
3481 installation_test_binary: Option<LanguageServerBinary>,
3482 cx: &mut ModelContext<Self>,
3483 ) {
3484 if !adapter.can_be_reinstalled() {
3485 log::info!(
3486 "Validation check requested for {:?} but it cannot be reinstalled",
3487 adapter.name.0
3488 );
3489 return;
3490 }
3491
3492 cx.spawn(move |this, mut cx| async move {
3493 log::info!("About to spawn test binary");
3494
3495 // A lack of test binary counts as a failure
3496 let process = installation_test_binary.and_then(|binary| {
3497 smol::process::Command::new(&binary.path)
3498 .current_dir(&binary.path)
3499 .args(binary.arguments)
3500 .stdin(Stdio::piped())
3501 .stdout(Stdio::piped())
3502 .stderr(Stdio::inherit())
3503 .kill_on_drop(true)
3504 .spawn()
3505 .ok()
3506 });
3507
3508 const PROCESS_TIMEOUT: Duration = Duration::from_secs(5);
3509 let mut timeout = cx.background_executor().timer(PROCESS_TIMEOUT).fuse();
3510
3511 let mut errored = false;
3512 if let Some(mut process) = process {
3513 futures::select! {
3514 status = process.status().fuse() => match status {
3515 Ok(status) => errored = !status.success(),
3516 Err(_) => errored = true,
3517 },
3518
3519 _ = timeout => {
3520 log::info!("test binary time-ed out, this counts as a success");
3521 _ = process.kill();
3522 }
3523 }
3524 } else {
3525 log::warn!("test binary failed to launch");
3526 errored = true;
3527 }
3528
3529 if errored {
3530 log::warn!("test binary check failed");
3531 let task = this
3532 .update(&mut cx, move |this, cx| {
3533 this.reinstall_language_server(language, adapter, server_id, cx)
3534 })
3535 .ok()
3536 .flatten();
3537
3538 if let Some(task) = task {
3539 task.await;
3540 }
3541 }
3542 })
3543 .detach();
3544 }
3545
3546 fn on_lsp_progress(
3547 &mut self,
3548 progress: lsp::ProgressParams,
3549 language_server_id: LanguageServerId,
3550 disk_based_diagnostics_progress_token: Option<String>,
3551 cx: &mut ModelContext<Self>,
3552 ) {
3553 let token = match progress.token {
3554 lsp::NumberOrString::String(token) => token,
3555 lsp::NumberOrString::Number(token) => {
3556 log::info!("skipping numeric progress token {}", token);
3557 return;
3558 }
3559 };
3560 let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
3561 let language_server_status =
3562 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3563 status
3564 } else {
3565 return;
3566 };
3567
3568 if !language_server_status.progress_tokens.contains(&token) {
3569 return;
3570 }
3571
3572 let is_disk_based_diagnostics_progress = disk_based_diagnostics_progress_token
3573 .as_ref()
3574 .map_or(false, |disk_based_token| {
3575 token.starts_with(disk_based_token)
3576 });
3577
3578 match progress {
3579 lsp::WorkDoneProgress::Begin(report) => {
3580 if is_disk_based_diagnostics_progress {
3581 language_server_status.has_pending_diagnostic_updates = true;
3582 self.disk_based_diagnostics_started(language_server_id, cx);
3583 self.buffer_ordered_messages_tx
3584 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3585 language_server_id,
3586 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(Default::default())
3587 })
3588 .ok();
3589 } else {
3590 self.on_lsp_work_start(
3591 language_server_id,
3592 token.clone(),
3593 LanguageServerProgress {
3594 message: report.message.clone(),
3595 percentage: report.percentage.map(|p| p as usize),
3596 last_update_at: Instant::now(),
3597 },
3598 cx,
3599 );
3600 self.buffer_ordered_messages_tx
3601 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3602 language_server_id,
3603 message: proto::update_language_server::Variant::WorkStart(
3604 proto::LspWorkStart {
3605 token,
3606 message: report.message,
3607 percentage: report.percentage.map(|p| p as u32),
3608 },
3609 ),
3610 })
3611 .ok();
3612 }
3613 }
3614 lsp::WorkDoneProgress::Report(report) => {
3615 if !is_disk_based_diagnostics_progress {
3616 self.on_lsp_work_progress(
3617 language_server_id,
3618 token.clone(),
3619 LanguageServerProgress {
3620 message: report.message.clone(),
3621 percentage: report.percentage.map(|p| p as usize),
3622 last_update_at: Instant::now(),
3623 },
3624 cx,
3625 );
3626 self.buffer_ordered_messages_tx
3627 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3628 language_server_id,
3629 message: proto::update_language_server::Variant::WorkProgress(
3630 proto::LspWorkProgress {
3631 token,
3632 message: report.message,
3633 percentage: report.percentage.map(|p| p as u32),
3634 },
3635 ),
3636 })
3637 .ok();
3638 }
3639 }
3640 lsp::WorkDoneProgress::End(_) => {
3641 language_server_status.progress_tokens.remove(&token);
3642
3643 if is_disk_based_diagnostics_progress {
3644 language_server_status.has_pending_diagnostic_updates = false;
3645 self.disk_based_diagnostics_finished(language_server_id, cx);
3646 self.buffer_ordered_messages_tx
3647 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3648 language_server_id,
3649 message:
3650 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
3651 Default::default(),
3652 ),
3653 })
3654 .ok();
3655 } else {
3656 self.on_lsp_work_end(language_server_id, token.clone(), cx);
3657 self.buffer_ordered_messages_tx
3658 .unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
3659 language_server_id,
3660 message: proto::update_language_server::Variant::WorkEnd(
3661 proto::LspWorkEnd { token },
3662 ),
3663 })
3664 .ok();
3665 }
3666 }
3667 }
3668 }
3669
3670 fn on_lsp_work_start(
3671 &mut self,
3672 language_server_id: LanguageServerId,
3673 token: String,
3674 progress: LanguageServerProgress,
3675 cx: &mut ModelContext<Self>,
3676 ) {
3677 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3678 status.pending_work.insert(token, progress);
3679 cx.notify();
3680 }
3681 }
3682
3683 fn on_lsp_work_progress(
3684 &mut self,
3685 language_server_id: LanguageServerId,
3686 token: String,
3687 progress: LanguageServerProgress,
3688 cx: &mut ModelContext<Self>,
3689 ) {
3690 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3691 let entry = status
3692 .pending_work
3693 .entry(token)
3694 .or_insert(LanguageServerProgress {
3695 message: Default::default(),
3696 percentage: Default::default(),
3697 last_update_at: progress.last_update_at,
3698 });
3699 if progress.message.is_some() {
3700 entry.message = progress.message;
3701 }
3702 if progress.percentage.is_some() {
3703 entry.percentage = progress.percentage;
3704 }
3705 entry.last_update_at = progress.last_update_at;
3706 cx.notify();
3707 }
3708 }
3709
3710 fn on_lsp_work_end(
3711 &mut self,
3712 language_server_id: LanguageServerId,
3713 token: String,
3714 cx: &mut ModelContext<Self>,
3715 ) {
3716 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
3717 cx.emit(Event::RefreshInlayHints);
3718 status.pending_work.remove(&token);
3719 cx.notify();
3720 }
3721 }
3722
3723 fn on_lsp_did_change_watched_files(
3724 &mut self,
3725 language_server_id: LanguageServerId,
3726 params: DidChangeWatchedFilesRegistrationOptions,
3727 cx: &mut ModelContext<Self>,
3728 ) {
3729 if let Some(LanguageServerState::Running { watched_paths, .. }) =
3730 self.language_servers.get_mut(&language_server_id)
3731 {
3732 let mut builders = HashMap::default();
3733 for watcher in params.watchers {
3734 for worktree in &self.worktrees {
3735 if let Some(worktree) = worktree.upgrade() {
3736 let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
3737 if let Some(abs_path) = tree.abs_path().to_str() {
3738 let relative_glob_pattern = match &watcher.glob_pattern {
3739 lsp::GlobPattern::String(s) => s
3740 .strip_prefix(abs_path)
3741 .and_then(|s| s.strip_prefix(std::path::MAIN_SEPARATOR)),
3742 lsp::GlobPattern::Relative(rp) => {
3743 let base_uri = match &rp.base_uri {
3744 lsp::OneOf::Left(workspace_folder) => {
3745 &workspace_folder.uri
3746 }
3747 lsp::OneOf::Right(base_uri) => base_uri,
3748 };
3749 base_uri.to_file_path().ok().and_then(|file_path| {
3750 (file_path.to_str() == Some(abs_path))
3751 .then_some(rp.pattern.as_str())
3752 })
3753 }
3754 };
3755 if let Some(relative_glob_pattern) = relative_glob_pattern {
3756 let literal_prefix = glob_literal_prefix(relative_glob_pattern);
3757 tree.as_local_mut()
3758 .unwrap()
3759 .add_path_prefix_to_scan(Path::new(literal_prefix).into());
3760 if let Some(glob) = Glob::new(relative_glob_pattern).log_err() {
3761 builders
3762 .entry(tree.id())
3763 .or_insert_with(|| GlobSetBuilder::new())
3764 .add(glob);
3765 }
3766 return true;
3767 }
3768 }
3769 false
3770 });
3771 if glob_is_inside_worktree {
3772 break;
3773 }
3774 }
3775 }
3776 }
3777
3778 watched_paths.clear();
3779 for (worktree_id, builder) in builders {
3780 if let Ok(globset) = builder.build() {
3781 watched_paths.insert(worktree_id, globset);
3782 }
3783 }
3784
3785 cx.notify();
3786 }
3787 }
3788
3789 async fn on_lsp_workspace_edit(
3790 this: WeakModel<Self>,
3791 params: lsp::ApplyWorkspaceEditParams,
3792 server_id: LanguageServerId,
3793 adapter: Arc<CachedLspAdapter>,
3794 mut cx: AsyncAppContext,
3795 ) -> Result<lsp::ApplyWorkspaceEditResponse> {
3796 let this = this
3797 .upgrade()
3798 .ok_or_else(|| anyhow!("project project closed"))?;
3799 let language_server = this
3800 .update(&mut cx, |this, _| this.language_server_for_id(server_id))?
3801 .ok_or_else(|| anyhow!("language server not found"))?;
3802 let transaction = Self::deserialize_workspace_edit(
3803 this.clone(),
3804 params.edit,
3805 true,
3806 adapter.clone(),
3807 language_server.clone(),
3808 &mut cx,
3809 )
3810 .await
3811 .log_err();
3812 this.update(&mut cx, |this, _| {
3813 if let Some(transaction) = transaction {
3814 this.last_workspace_edits_by_language_server
3815 .insert(server_id, transaction);
3816 }
3817 })?;
3818 Ok(lsp::ApplyWorkspaceEditResponse {
3819 applied: true,
3820 failed_change: None,
3821 failure_reason: None,
3822 })
3823 }
3824
3825 pub fn language_server_statuses(
3826 &self,
3827 ) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
3828 self.language_server_statuses.values()
3829 }
3830
3831 pub fn update_diagnostics(
3832 &mut self,
3833 language_server_id: LanguageServerId,
3834 mut params: lsp::PublishDiagnosticsParams,
3835 disk_based_sources: &[String],
3836 cx: &mut ModelContext<Self>,
3837 ) -> Result<()> {
3838 let abs_path = params
3839 .uri
3840 .to_file_path()
3841 .map_err(|_| anyhow!("URI is not a file"))?;
3842 let mut diagnostics = Vec::default();
3843 let mut primary_diagnostic_group_ids = HashMap::default();
3844 let mut sources_by_group_id = HashMap::default();
3845 let mut supporting_diagnostics = HashMap::default();
3846
3847 // Ensure that primary diagnostics are always the most severe
3848 params.diagnostics.sort_by_key(|item| item.severity);
3849
3850 for diagnostic in ¶ms.diagnostics {
3851 let source = diagnostic.source.as_ref();
3852 let code = diagnostic.code.as_ref().map(|code| match code {
3853 lsp::NumberOrString::Number(code) => code.to_string(),
3854 lsp::NumberOrString::String(code) => code.clone(),
3855 });
3856 let range = range_from_lsp(diagnostic.range);
3857 let is_supporting = diagnostic
3858 .related_information
3859 .as_ref()
3860 .map_or(false, |infos| {
3861 infos.iter().any(|info| {
3862 primary_diagnostic_group_ids.contains_key(&(
3863 source,
3864 code.clone(),
3865 range_from_lsp(info.location.range),
3866 ))
3867 })
3868 });
3869
3870 let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
3871 tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
3872 });
3873
3874 if is_supporting {
3875 supporting_diagnostics.insert(
3876 (source, code.clone(), range),
3877 (diagnostic.severity, is_unnecessary),
3878 );
3879 } else {
3880 let group_id = post_inc(&mut self.next_diagnostic_group_id);
3881 let is_disk_based =
3882 source.map_or(false, |source| disk_based_sources.contains(source));
3883
3884 sources_by_group_id.insert(group_id, source);
3885 primary_diagnostic_group_ids
3886 .insert((source, code.clone(), range.clone()), group_id);
3887
3888 diagnostics.push(DiagnosticEntry {
3889 range,
3890 diagnostic: Diagnostic {
3891 source: diagnostic.source.clone(),
3892 code: code.clone(),
3893 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
3894 message: diagnostic.message.trim().to_string(),
3895 group_id,
3896 is_primary: true,
3897 is_disk_based,
3898 is_unnecessary,
3899 },
3900 });
3901 if let Some(infos) = &diagnostic.related_information {
3902 for info in infos {
3903 if info.location.uri == params.uri && !info.message.is_empty() {
3904 let range = range_from_lsp(info.location.range);
3905 diagnostics.push(DiagnosticEntry {
3906 range,
3907 diagnostic: Diagnostic {
3908 source: diagnostic.source.clone(),
3909 code: code.clone(),
3910 severity: DiagnosticSeverity::INFORMATION,
3911 message: info.message.trim().to_string(),
3912 group_id,
3913 is_primary: false,
3914 is_disk_based,
3915 is_unnecessary: false,
3916 },
3917 });
3918 }
3919 }
3920 }
3921 }
3922 }
3923
3924 for entry in &mut diagnostics {
3925 let diagnostic = &mut entry.diagnostic;
3926 if !diagnostic.is_primary {
3927 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
3928 if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
3929 source,
3930 diagnostic.code.clone(),
3931 entry.range.clone(),
3932 )) {
3933 if let Some(severity) = severity {
3934 diagnostic.severity = severity;
3935 }
3936 diagnostic.is_unnecessary = is_unnecessary;
3937 }
3938 }
3939 }
3940
3941 self.update_diagnostic_entries(
3942 language_server_id,
3943 abs_path,
3944 params.version,
3945 diagnostics,
3946 cx,
3947 )?;
3948 Ok(())
3949 }
3950
3951 pub fn update_diagnostic_entries(
3952 &mut self,
3953 server_id: LanguageServerId,
3954 abs_path: PathBuf,
3955 version: Option<i32>,
3956 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
3957 cx: &mut ModelContext<Project>,
3958 ) -> Result<(), anyhow::Error> {
3959 let (worktree, relative_path) = self
3960 .find_local_worktree(&abs_path, cx)
3961 .ok_or_else(|| anyhow!("no worktree found for diagnostics path {abs_path:?}"))?;
3962
3963 let project_path = ProjectPath {
3964 worktree_id: worktree.read(cx).id(),
3965 path: relative_path.into(),
3966 };
3967
3968 if let Some(buffer) = self.get_open_buffer(&project_path, cx) {
3969 self.update_buffer_diagnostics(&buffer, server_id, version, diagnostics.clone(), cx)?;
3970 }
3971
3972 let updated = worktree.update(cx, |worktree, cx| {
3973 worktree
3974 .as_local_mut()
3975 .ok_or_else(|| anyhow!("not a local worktree"))?
3976 .update_diagnostics(server_id, project_path.path.clone(), diagnostics, cx)
3977 })?;
3978 if updated {
3979 cx.emit(Event::DiagnosticsUpdated {
3980 language_server_id: server_id,
3981 path: project_path,
3982 });
3983 }
3984 Ok(())
3985 }
3986
3987 fn update_buffer_diagnostics(
3988 &mut self,
3989 buffer: &Model<Buffer>,
3990 server_id: LanguageServerId,
3991 version: Option<i32>,
3992 mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
3993 cx: &mut ModelContext<Self>,
3994 ) -> Result<()> {
3995 fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
3996 Ordering::Equal
3997 .then_with(|| b.is_primary.cmp(&a.is_primary))
3998 .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
3999 .then_with(|| a.severity.cmp(&b.severity))
4000 .then_with(|| a.message.cmp(&b.message))
4001 }
4002
4003 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx)?;
4004
4005 diagnostics.sort_unstable_by(|a, b| {
4006 Ordering::Equal
4007 .then_with(|| a.range.start.cmp(&b.range.start))
4008 .then_with(|| b.range.end.cmp(&a.range.end))
4009 .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
4010 });
4011
4012 let mut sanitized_diagnostics = Vec::new();
4013 let edits_since_save = Patch::new(
4014 snapshot
4015 .edits_since::<Unclipped<PointUtf16>>(buffer.read(cx).saved_version())
4016 .collect(),
4017 );
4018 for entry in diagnostics {
4019 let start;
4020 let end;
4021 if entry.diagnostic.is_disk_based {
4022 // Some diagnostics are based on files on disk instead of buffers'
4023 // current contents. Adjust these diagnostics' ranges to reflect
4024 // any unsaved edits.
4025 start = edits_since_save.old_to_new(entry.range.start);
4026 end = edits_since_save.old_to_new(entry.range.end);
4027 } else {
4028 start = entry.range.start;
4029 end = entry.range.end;
4030 }
4031
4032 let mut range = snapshot.clip_point_utf16(start, Bias::Left)
4033 ..snapshot.clip_point_utf16(end, Bias::Right);
4034
4035 // Expand empty ranges by one codepoint
4036 if range.start == range.end {
4037 // This will be go to the next boundary when being clipped
4038 range.end.column += 1;
4039 range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right);
4040 if range.start == range.end && range.end.column > 0 {
4041 range.start.column -= 1;
4042 range.start = snapshot.clip_point_utf16(Unclipped(range.start), Bias::Left);
4043 }
4044 }
4045
4046 sanitized_diagnostics.push(DiagnosticEntry {
4047 range,
4048 diagnostic: entry.diagnostic,
4049 });
4050 }
4051 drop(edits_since_save);
4052
4053 let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
4054 buffer.update(cx, |buffer, cx| {
4055 buffer.update_diagnostics(server_id, set, cx)
4056 });
4057 Ok(())
4058 }
4059
4060 pub fn reload_buffers(
4061 &self,
4062 buffers: HashSet<Model<Buffer>>,
4063 push_to_history: bool,
4064 cx: &mut ModelContext<Self>,
4065 ) -> Task<Result<ProjectTransaction>> {
4066 let mut local_buffers = Vec::new();
4067 let mut remote_buffers = None;
4068 for buffer_handle in buffers {
4069 let buffer = buffer_handle.read(cx);
4070 if buffer.is_dirty() {
4071 if let Some(file) = File::from_dyn(buffer.file()) {
4072 if file.is_local() {
4073 local_buffers.push(buffer_handle);
4074 } else {
4075 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
4076 }
4077 }
4078 }
4079 }
4080
4081 let remote_buffers = self.remote_id().zip(remote_buffers);
4082 let client = self.client.clone();
4083
4084 cx.spawn(move |this, mut cx| async move {
4085 let mut project_transaction = ProjectTransaction::default();
4086
4087 if let Some((project_id, remote_buffers)) = remote_buffers {
4088 let response = client
4089 .request(proto::ReloadBuffers {
4090 project_id,
4091 buffer_ids: remote_buffers
4092 .iter()
4093 .filter_map(|buffer| {
4094 buffer
4095 .update(&mut cx, |buffer, _| buffer.remote_id().into())
4096 .ok()
4097 })
4098 .collect(),
4099 })
4100 .await?
4101 .transaction
4102 .ok_or_else(|| anyhow!("missing transaction"))?;
4103 project_transaction = this
4104 .update(&mut cx, |this, cx| {
4105 this.deserialize_project_transaction(response, push_to_history, cx)
4106 })?
4107 .await?;
4108 }
4109
4110 for buffer in local_buffers {
4111 let transaction = buffer
4112 .update(&mut cx, |buffer, cx| buffer.reload(cx))?
4113 .await?;
4114 buffer.update(&mut cx, |buffer, cx| {
4115 if let Some(transaction) = transaction {
4116 if !push_to_history {
4117 buffer.forget_transaction(transaction.id);
4118 }
4119 project_transaction.0.insert(cx.handle(), transaction);
4120 }
4121 })?;
4122 }
4123
4124 Ok(project_transaction)
4125 })
4126 }
4127
4128 pub fn format(
4129 &mut self,
4130 buffers: HashSet<Model<Buffer>>,
4131 push_to_history: bool,
4132 trigger: FormatTrigger,
4133 cx: &mut ModelContext<Project>,
4134 ) -> Task<anyhow::Result<ProjectTransaction>> {
4135 if self.is_local() {
4136 let mut buffers_with_paths = buffers
4137 .into_iter()
4138 .filter_map(|buffer_handle| {
4139 let buffer = buffer_handle.read(cx);
4140 let file = File::from_dyn(buffer.file())?;
4141 let buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
4142 Some((buffer_handle, buffer_abs_path))
4143 })
4144 .collect::<Vec<_>>();
4145
4146 cx.spawn(move |project, mut cx| async move {
4147 // Do not allow multiple concurrent formatting requests for the
4148 // same buffer.
4149 project.update(&mut cx, |this, cx| {
4150 buffers_with_paths.retain(|(buffer, _)| {
4151 this.buffers_being_formatted
4152 .insert(buffer.read(cx).remote_id())
4153 });
4154 })?;
4155
4156 let _cleanup = defer({
4157 let this = project.clone();
4158 let mut cx = cx.clone();
4159 let buffers = &buffers_with_paths;
4160 move || {
4161 this.update(&mut cx, |this, cx| {
4162 for (buffer, _) in buffers {
4163 this.buffers_being_formatted
4164 .remove(&buffer.read(cx).remote_id());
4165 }
4166 })
4167 .ok();
4168 }
4169 });
4170
4171 let mut project_transaction = ProjectTransaction::default();
4172 for (buffer, buffer_abs_path) in &buffers_with_paths {
4173 let adapters_and_servers: Vec<_> = project.update(&mut cx, |project, cx| {
4174 project
4175 .language_servers_for_buffer(&buffer.read(cx), cx)
4176 .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()))
4177 .collect()
4178 })?;
4179
4180 let settings = buffer.update(&mut cx, |buffer, cx| {
4181 language_settings(buffer.language(), buffer.file(), cx).clone()
4182 })?;
4183
4184 let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
4185 let ensure_final_newline = settings.ensure_final_newline_on_save;
4186 let tab_size = settings.tab_size;
4187
4188 // First, format buffer's whitespace according to the settings.
4189 let trailing_whitespace_diff = if remove_trailing_whitespace {
4190 Some(
4191 buffer
4192 .update(&mut cx, |b, cx| b.remove_trailing_whitespace(cx))?
4193 .await,
4194 )
4195 } else {
4196 None
4197 };
4198 let whitespace_transaction_id = buffer.update(&mut cx, |buffer, cx| {
4199 buffer.finalize_last_transaction();
4200 buffer.start_transaction();
4201 if let Some(diff) = trailing_whitespace_diff {
4202 buffer.apply_diff(diff, cx);
4203 }
4204 if ensure_final_newline {
4205 buffer.ensure_final_newline(cx);
4206 }
4207 buffer.end_transaction(cx)
4208 })?;
4209
4210 for (lsp_adapter, language_server) in adapters_and_servers.iter() {
4211 // Apply the code actions on
4212 let code_actions: Vec<lsp::CodeActionKind> = settings
4213 .code_actions_on_format
4214 .iter()
4215 .flat_map(|(kind, enabled)| {
4216 if *enabled {
4217 Some(kind.clone().into())
4218 } else {
4219 None
4220 }
4221 })
4222 .collect();
4223
4224 if !code_actions.is_empty()
4225 && !(trigger == FormatTrigger::Save
4226 && settings.format_on_save == FormatOnSave::Off)
4227 {
4228 let actions = project
4229 .update(&mut cx, |this, cx| {
4230 this.request_lsp(
4231 buffer.clone(),
4232 LanguageServerToQuery::Other(language_server.server_id()),
4233 GetCodeActions {
4234 range: text::Anchor::MIN..text::Anchor::MAX,
4235 kinds: Some(code_actions),
4236 },
4237 cx,
4238 )
4239 })?
4240 .await?;
4241
4242 for action in actions {
4243 if let Some(edit) = action.lsp_action.edit {
4244 if edit.changes.is_none() && edit.document_changes.is_none() {
4245 continue;
4246 }
4247
4248 let new = Self::deserialize_workspace_edit(
4249 project
4250 .upgrade()
4251 .ok_or_else(|| anyhow!("project dropped"))?,
4252 edit,
4253 push_to_history,
4254 lsp_adapter.clone(),
4255 language_server.clone(),
4256 &mut cx,
4257 )
4258 .await?;
4259 project_transaction.0.extend(new.0);
4260 }
4261
4262 if let Some(command) = action.lsp_action.command {
4263 project.update(&mut cx, |this, _| {
4264 this.last_workspace_edits_by_language_server
4265 .remove(&language_server.server_id());
4266 })?;
4267
4268 language_server
4269 .request::<lsp::request::ExecuteCommand>(
4270 lsp::ExecuteCommandParams {
4271 command: command.command,
4272 arguments: command.arguments.unwrap_or_default(),
4273 ..Default::default()
4274 },
4275 )
4276 .await?;
4277
4278 project.update(&mut cx, |this, _| {
4279 project_transaction.0.extend(
4280 this.last_workspace_edits_by_language_server
4281 .remove(&language_server.server_id())
4282 .unwrap_or_default()
4283 .0,
4284 )
4285 })?;
4286 }
4287 }
4288 }
4289 }
4290
4291 // Apply language-specific formatting using either the primary language server
4292 // or external command.
4293 let primary_language_server = adapters_and_servers
4294 .first()
4295 .cloned()
4296 .map(|(_, lsp)| lsp.clone());
4297 let server_and_buffer = primary_language_server
4298 .as_ref()
4299 .zip(buffer_abs_path.as_ref());
4300
4301 let mut format_operation = None;
4302 match (&settings.formatter, &settings.format_on_save) {
4303 (_, FormatOnSave::Off) if trigger == FormatTrigger::Save => {}
4304
4305 (Formatter::LanguageServer, FormatOnSave::On | FormatOnSave::Off)
4306 | (_, FormatOnSave::LanguageServer) => {
4307 if let Some((language_server, buffer_abs_path)) = server_and_buffer {
4308 format_operation = Some(FormatOperation::Lsp(
4309 Self::format_via_lsp(
4310 &project,
4311 buffer,
4312 buffer_abs_path,
4313 language_server,
4314 tab_size,
4315 &mut cx,
4316 )
4317 .await
4318 .context("failed to format via language server")?,
4319 ));
4320 }
4321 }
4322
4323 (
4324 Formatter::External { command, arguments },
4325 FormatOnSave::On | FormatOnSave::Off,
4326 )
4327 | (_, FormatOnSave::External { command, arguments }) => {
4328 if let Some(buffer_abs_path) = buffer_abs_path {
4329 format_operation = Self::format_via_external_command(
4330 buffer,
4331 buffer_abs_path,
4332 command,
4333 arguments,
4334 &mut cx,
4335 )
4336 .await
4337 .context(format!(
4338 "failed to format via external command {:?}",
4339 command
4340 ))?
4341 .map(FormatOperation::External);
4342 }
4343 }
4344 (Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => {
4345 if let Some(new_operation) =
4346 prettier_support::format_with_prettier(&project, buffer, &mut cx)
4347 .await
4348 {
4349 format_operation = Some(new_operation);
4350 } else if let Some((language_server, buffer_abs_path)) =
4351 server_and_buffer
4352 {
4353 format_operation = Some(FormatOperation::Lsp(
4354 Self::format_via_lsp(
4355 &project,
4356 buffer,
4357 buffer_abs_path,
4358 language_server,
4359 tab_size,
4360 &mut cx,
4361 )
4362 .await
4363 .context("failed to format via language server")?,
4364 ));
4365 }
4366 }
4367 (Formatter::Prettier, FormatOnSave::On | FormatOnSave::Off) => {
4368 if let Some(new_operation) =
4369 prettier_support::format_with_prettier(&project, buffer, &mut cx)
4370 .await
4371 {
4372 format_operation = Some(new_operation);
4373 }
4374 }
4375 };
4376
4377 buffer.update(&mut cx, |b, cx| {
4378 // If the buffer had its whitespace formatted and was edited while the language-specific
4379 // formatting was being computed, avoid applying the language-specific formatting, because
4380 // it can't be grouped with the whitespace formatting in the undo history.
4381 if let Some(transaction_id) = whitespace_transaction_id {
4382 if b.peek_undo_stack()
4383 .map_or(true, |e| e.transaction_id() != transaction_id)
4384 {
4385 format_operation.take();
4386 }
4387 }
4388
4389 // Apply any language-specific formatting, and group the two formatting operations
4390 // in the buffer's undo history.
4391 if let Some(operation) = format_operation {
4392 match operation {
4393 FormatOperation::Lsp(edits) => {
4394 b.edit(edits, None, cx);
4395 }
4396 FormatOperation::External(diff) => {
4397 b.apply_diff(diff, cx);
4398 }
4399 FormatOperation::Prettier(diff) => {
4400 b.apply_diff(diff, cx);
4401 }
4402 }
4403
4404 if let Some(transaction_id) = whitespace_transaction_id {
4405 b.group_until_transaction(transaction_id);
4406 } else if let Some(transaction) = project_transaction.0.get(buffer) {
4407 b.group_until_transaction(transaction.id)
4408 }
4409 }
4410
4411 if let Some(transaction) = b.finalize_last_transaction().cloned() {
4412 if !push_to_history {
4413 b.forget_transaction(transaction.id);
4414 }
4415 project_transaction.0.insert(buffer.clone(), transaction);
4416 }
4417 })?;
4418 }
4419
4420 Ok(project_transaction)
4421 })
4422 } else {
4423 let remote_id = self.remote_id();
4424 let client = self.client.clone();
4425 cx.spawn(move |this, mut cx| async move {
4426 let mut project_transaction = ProjectTransaction::default();
4427 if let Some(project_id) = remote_id {
4428 let response = client
4429 .request(proto::FormatBuffers {
4430 project_id,
4431 trigger: trigger as i32,
4432 buffer_ids: buffers
4433 .iter()
4434 .map(|buffer| {
4435 buffer.update(&mut cx, |buffer, _| buffer.remote_id().into())
4436 })
4437 .collect::<Result<_>>()?,
4438 })
4439 .await?
4440 .transaction
4441 .ok_or_else(|| anyhow!("missing transaction"))?;
4442 project_transaction = this
4443 .update(&mut cx, |this, cx| {
4444 this.deserialize_project_transaction(response, push_to_history, cx)
4445 })?
4446 .await?;
4447 }
4448 Ok(project_transaction)
4449 })
4450 }
4451 }
4452
4453 async fn format_via_lsp(
4454 this: &WeakModel<Self>,
4455 buffer: &Model<Buffer>,
4456 abs_path: &Path,
4457 language_server: &Arc<LanguageServer>,
4458 tab_size: NonZeroU32,
4459 cx: &mut AsyncAppContext,
4460 ) -> Result<Vec<(Range<Anchor>, String)>> {
4461 let uri = lsp::Url::from_file_path(abs_path)
4462 .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
4463 let text_document = lsp::TextDocumentIdentifier::new(uri);
4464 let capabilities = &language_server.capabilities();
4465
4466 let formatting_provider = capabilities.document_formatting_provider.as_ref();
4467 let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
4468
4469 let lsp_edits = if matches!(formatting_provider, Some(p) if *p != OneOf::Left(false)) {
4470 language_server
4471 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
4472 text_document,
4473 options: lsp_command::lsp_formatting_options(tab_size.get()),
4474 work_done_progress_params: Default::default(),
4475 })
4476 .await?
4477 } else if matches!(range_formatting_provider, Some(p) if *p != OneOf::Left(false)) {
4478 let buffer_start = lsp::Position::new(0, 0);
4479 let buffer_end = buffer.update(cx, |b, _| point_to_lsp(b.max_point_utf16()))?;
4480
4481 language_server
4482 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
4483 text_document,
4484 range: lsp::Range::new(buffer_start, buffer_end),
4485 options: lsp_command::lsp_formatting_options(tab_size.get()),
4486 work_done_progress_params: Default::default(),
4487 })
4488 .await?
4489 } else {
4490 None
4491 };
4492
4493 if let Some(lsp_edits) = lsp_edits {
4494 this.update(cx, |this, cx| {
4495 this.edits_from_lsp(buffer, lsp_edits, language_server.server_id(), None, cx)
4496 })?
4497 .await
4498 } else {
4499 Ok(Vec::new())
4500 }
4501 }
4502
4503 async fn format_via_external_command(
4504 buffer: &Model<Buffer>,
4505 buffer_abs_path: &Path,
4506 command: &str,
4507 arguments: &[String],
4508 cx: &mut AsyncAppContext,
4509 ) -> Result<Option<Diff>> {
4510 let working_dir_path = buffer.update(cx, |buffer, cx| {
4511 let file = File::from_dyn(buffer.file())?;
4512 let worktree = file.worktree.read(cx).as_local()?;
4513 let mut worktree_path = worktree.abs_path().to_path_buf();
4514 if worktree.root_entry()?.is_file() {
4515 worktree_path.pop();
4516 }
4517 Some(worktree_path)
4518 })?;
4519
4520 if let Some(working_dir_path) = working_dir_path {
4521 let mut child =
4522 smol::process::Command::new(command)
4523 .args(arguments.iter().map(|arg| {
4524 arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
4525 }))
4526 .current_dir(&working_dir_path)
4527 .stdin(smol::process::Stdio::piped())
4528 .stdout(smol::process::Stdio::piped())
4529 .stderr(smol::process::Stdio::piped())
4530 .spawn()?;
4531 let stdin = child
4532 .stdin
4533 .as_mut()
4534 .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
4535 let text = buffer.update(cx, |buffer, _| buffer.as_rope().clone())?;
4536 for chunk in text.chunks() {
4537 stdin.write_all(chunk.as_bytes()).await?;
4538 }
4539 stdin.flush().await?;
4540
4541 let output = child.output().await?;
4542 if !output.status.success() {
4543 return Err(anyhow!(
4544 "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
4545 output.status.code(),
4546 String::from_utf8_lossy(&output.stdout),
4547 String::from_utf8_lossy(&output.stderr),
4548 ));
4549 }
4550
4551 let stdout = String::from_utf8(output.stdout)?;
4552 Ok(Some(
4553 buffer
4554 .update(cx, |buffer, cx| buffer.diff(stdout, cx))?
4555 .await,
4556 ))
4557 } else {
4558 Ok(None)
4559 }
4560 }
4561
4562 #[inline(never)]
4563 fn definition_impl(
4564 &self,
4565 buffer: &Model<Buffer>,
4566 position: PointUtf16,
4567 cx: &mut ModelContext<Self>,
4568 ) -> Task<Result<Vec<LocationLink>>> {
4569 self.request_lsp(
4570 buffer.clone(),
4571 LanguageServerToQuery::Primary,
4572 GetDefinition { position },
4573 cx,
4574 )
4575 }
4576 pub fn definition<T: ToPointUtf16>(
4577 &self,
4578 buffer: &Model<Buffer>,
4579 position: T,
4580 cx: &mut ModelContext<Self>,
4581 ) -> Task<Result<Vec<LocationLink>>> {
4582 let position = position.to_point_utf16(buffer.read(cx));
4583 self.definition_impl(buffer, position, cx)
4584 }
4585
4586 fn type_definition_impl(
4587 &self,
4588 buffer: &Model<Buffer>,
4589 position: PointUtf16,
4590 cx: &mut ModelContext<Self>,
4591 ) -> Task<Result<Vec<LocationLink>>> {
4592 self.request_lsp(
4593 buffer.clone(),
4594 LanguageServerToQuery::Primary,
4595 GetTypeDefinition { position },
4596 cx,
4597 )
4598 }
4599
4600 pub fn type_definition<T: ToPointUtf16>(
4601 &self,
4602 buffer: &Model<Buffer>,
4603 position: T,
4604 cx: &mut ModelContext<Self>,
4605 ) -> Task<Result<Vec<LocationLink>>> {
4606 let position = position.to_point_utf16(buffer.read(cx));
4607 self.type_definition_impl(buffer, position, cx)
4608 }
4609
4610 fn implementation_impl(
4611 &self,
4612 buffer: &Model<Buffer>,
4613 position: PointUtf16,
4614 cx: &mut ModelContext<Self>,
4615 ) -> Task<Result<Vec<LocationLink>>> {
4616 self.request_lsp(
4617 buffer.clone(),
4618 LanguageServerToQuery::Primary,
4619 GetImplementation { position },
4620 cx,
4621 )
4622 }
4623
4624 pub fn implementation<T: ToPointUtf16>(
4625 &self,
4626 buffer: &Model<Buffer>,
4627 position: T,
4628 cx: &mut ModelContext<Self>,
4629 ) -> Task<Result<Vec<LocationLink>>> {
4630 let position = position.to_point_utf16(buffer.read(cx));
4631 self.implementation_impl(buffer, position, cx)
4632 }
4633
4634 fn references_impl(
4635 &self,
4636 buffer: &Model<Buffer>,
4637 position: PointUtf16,
4638 cx: &mut ModelContext<Self>,
4639 ) -> Task<Result<Vec<Location>>> {
4640 self.request_lsp(
4641 buffer.clone(),
4642 LanguageServerToQuery::Primary,
4643 GetReferences { position },
4644 cx,
4645 )
4646 }
4647 pub fn references<T: ToPointUtf16>(
4648 &self,
4649 buffer: &Model<Buffer>,
4650 position: T,
4651 cx: &mut ModelContext<Self>,
4652 ) -> Task<Result<Vec<Location>>> {
4653 let position = position.to_point_utf16(buffer.read(cx));
4654 self.references_impl(buffer, position, cx)
4655 }
4656
4657 fn document_highlights_impl(
4658 &self,
4659 buffer: &Model<Buffer>,
4660 position: PointUtf16,
4661 cx: &mut ModelContext<Self>,
4662 ) -> Task<Result<Vec<DocumentHighlight>>> {
4663 self.request_lsp(
4664 buffer.clone(),
4665 LanguageServerToQuery::Primary,
4666 GetDocumentHighlights { position },
4667 cx,
4668 )
4669 }
4670
4671 pub fn document_highlights<T: ToPointUtf16>(
4672 &self,
4673 buffer: &Model<Buffer>,
4674 position: T,
4675 cx: &mut ModelContext<Self>,
4676 ) -> Task<Result<Vec<DocumentHighlight>>> {
4677 let position = position.to_point_utf16(buffer.read(cx));
4678 self.document_highlights_impl(buffer, position, cx)
4679 }
4680
4681 pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
4682 if self.is_local() {
4683 let mut requests = Vec::new();
4684 for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
4685 let worktree_id = *worktree_id;
4686 let worktree_handle = self.worktree_for_id(worktree_id, cx);
4687 let worktree = match worktree_handle.and_then(|tree| tree.read(cx).as_local()) {
4688 Some(worktree) => worktree,
4689 None => continue,
4690 };
4691 let worktree_abs_path = worktree.abs_path().clone();
4692
4693 let (adapter, language, server) = match self.language_servers.get(server_id) {
4694 Some(LanguageServerState::Running {
4695 adapter,
4696 language,
4697 server,
4698 ..
4699 }) => (adapter.clone(), language.clone(), server),
4700
4701 _ => continue,
4702 };
4703
4704 requests.push(
4705 server
4706 .request::<lsp::request::WorkspaceSymbolRequest>(
4707 lsp::WorkspaceSymbolParams {
4708 query: query.to_string(),
4709 ..Default::default()
4710 },
4711 )
4712 .log_err()
4713 .map(move |response| {
4714 let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response {
4715 lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
4716 flat_responses.into_iter().map(|lsp_symbol| {
4717 (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
4718 }).collect::<Vec<_>>()
4719 }
4720 lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
4721 nested_responses.into_iter().filter_map(|lsp_symbol| {
4722 let location = match lsp_symbol.location {
4723 OneOf::Left(location) => location,
4724 OneOf::Right(_) => {
4725 error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport");
4726 return None
4727 }
4728 };
4729 Some((lsp_symbol.name, lsp_symbol.kind, location))
4730 }).collect::<Vec<_>>()
4731 }
4732 }).unwrap_or_default();
4733
4734 (
4735 adapter,
4736 language,
4737 worktree_id,
4738 worktree_abs_path,
4739 lsp_symbols,
4740 )
4741 }),
4742 );
4743 }
4744
4745 cx.spawn(move |this, mut cx| async move {
4746 let responses = futures::future::join_all(requests).await;
4747 let this = match this.upgrade() {
4748 Some(this) => this,
4749 None => return Ok(Vec::new()),
4750 };
4751
4752 let symbols = this.update(&mut cx, |this, cx| {
4753 let mut symbols = Vec::new();
4754 for (
4755 adapter,
4756 adapter_language,
4757 source_worktree_id,
4758 worktree_abs_path,
4759 lsp_symbols,
4760 ) in responses
4761 {
4762 symbols.extend(lsp_symbols.into_iter().filter_map(
4763 |(symbol_name, symbol_kind, symbol_location)| {
4764 let abs_path = symbol_location.uri.to_file_path().ok()?;
4765 let mut worktree_id = source_worktree_id;
4766 let path;
4767 if let Some((worktree, rel_path)) =
4768 this.find_local_worktree(&abs_path, cx)
4769 {
4770 worktree_id = worktree.read(cx).id();
4771 path = rel_path;
4772 } else {
4773 path = relativize_path(&worktree_abs_path, &abs_path);
4774 }
4775
4776 let project_path = ProjectPath {
4777 worktree_id,
4778 path: path.into(),
4779 };
4780 let signature = this.symbol_signature(&project_path);
4781 let adapter_language = adapter_language.clone();
4782 let language = this
4783 .languages
4784 .language_for_file(&project_path.path, None)
4785 .unwrap_or_else(move |_| adapter_language);
4786 let language_server_name = adapter.name.clone();
4787 Some(async move {
4788 let language = language.await;
4789 let label =
4790 language.label_for_symbol(&symbol_name, symbol_kind).await;
4791
4792 Symbol {
4793 language_server_name,
4794 source_worktree_id,
4795 path: project_path,
4796 label: label.unwrap_or_else(|| {
4797 CodeLabel::plain(symbol_name.clone(), None)
4798 }),
4799 kind: symbol_kind,
4800 name: symbol_name,
4801 range: range_from_lsp(symbol_location.range),
4802 signature,
4803 }
4804 })
4805 },
4806 ));
4807 }
4808
4809 symbols
4810 })?;
4811
4812 Ok(futures::future::join_all(symbols).await)
4813 })
4814 } else if let Some(project_id) = self.remote_id() {
4815 let request = self.client.request(proto::GetProjectSymbols {
4816 project_id,
4817 query: query.to_string(),
4818 });
4819 cx.spawn(move |this, mut cx| async move {
4820 let response = request.await?;
4821 let mut symbols = Vec::new();
4822 if let Some(this) = this.upgrade() {
4823 let new_symbols = this.update(&mut cx, |this, _| {
4824 response
4825 .symbols
4826 .into_iter()
4827 .map(|symbol| this.deserialize_symbol(symbol))
4828 .collect::<Vec<_>>()
4829 })?;
4830 symbols = futures::future::join_all(new_symbols)
4831 .await
4832 .into_iter()
4833 .filter_map(|symbol| symbol.log_err())
4834 .collect::<Vec<_>>();
4835 }
4836 Ok(symbols)
4837 })
4838 } else {
4839 Task::ready(Ok(Default::default()))
4840 }
4841 }
4842
4843 pub fn open_buffer_for_symbol(
4844 &mut self,
4845 symbol: &Symbol,
4846 cx: &mut ModelContext<Self>,
4847 ) -> Task<Result<Model<Buffer>>> {
4848 if self.is_local() {
4849 let language_server_id = if let Some(id) = self.language_server_ids.get(&(
4850 symbol.source_worktree_id,
4851 symbol.language_server_name.clone(),
4852 )) {
4853 *id
4854 } else {
4855 return Task::ready(Err(anyhow!(
4856 "language server for worktree and language not found"
4857 )));
4858 };
4859
4860 let worktree_abs_path = if let Some(worktree_abs_path) = self
4861 .worktree_for_id(symbol.path.worktree_id, cx)
4862 .and_then(|worktree| worktree.read(cx).as_local())
4863 .map(|local_worktree| local_worktree.abs_path())
4864 {
4865 worktree_abs_path
4866 } else {
4867 return Task::ready(Err(anyhow!("worktree not found for symbol")));
4868 };
4869
4870 let symbol_abs_path = resolve_path(worktree_abs_path, &symbol.path.path);
4871 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
4872 uri
4873 } else {
4874 return Task::ready(Err(anyhow!("invalid symbol path")));
4875 };
4876
4877 self.open_local_buffer_via_lsp(
4878 symbol_uri,
4879 language_server_id,
4880 symbol.language_server_name.clone(),
4881 cx,
4882 )
4883 } else if let Some(project_id) = self.remote_id() {
4884 let request = self.client.request(proto::OpenBufferForSymbol {
4885 project_id,
4886 symbol: Some(serialize_symbol(symbol)),
4887 });
4888 cx.spawn(move |this, mut cx| async move {
4889 let response = request.await?;
4890 let buffer_id = BufferId::new(response.buffer_id)?;
4891 this.update(&mut cx, |this, cx| {
4892 this.wait_for_remote_buffer(buffer_id, cx)
4893 })?
4894 .await
4895 })
4896 } else {
4897 Task::ready(Err(anyhow!("project does not have a remote id")))
4898 }
4899 }
4900
4901 fn hover_impl(
4902 &self,
4903 buffer: &Model<Buffer>,
4904 position: PointUtf16,
4905 cx: &mut ModelContext<Self>,
4906 ) -> Task<Result<Option<Hover>>> {
4907 self.request_lsp(
4908 buffer.clone(),
4909 LanguageServerToQuery::Primary,
4910 GetHover { position },
4911 cx,
4912 )
4913 }
4914 pub fn hover<T: ToPointUtf16>(
4915 &self,
4916 buffer: &Model<Buffer>,
4917 position: T,
4918 cx: &mut ModelContext<Self>,
4919 ) -> Task<Result<Option<Hover>>> {
4920 let position = position.to_point_utf16(buffer.read(cx));
4921 self.hover_impl(buffer, position, cx)
4922 }
4923
4924 #[inline(never)]
4925 fn completions_impl(
4926 &self,
4927 buffer: &Model<Buffer>,
4928 position: PointUtf16,
4929 cx: &mut ModelContext<Self>,
4930 ) -> Task<Result<Vec<Completion>>> {
4931 if self.is_local() {
4932 let snapshot = buffer.read(cx).snapshot();
4933 let offset = position.to_offset(&snapshot);
4934 let scope = snapshot.language_scope_at(offset);
4935
4936 let server_ids: Vec<_> = self
4937 .language_servers_for_buffer(buffer.read(cx), cx)
4938 .filter(|(_, server)| server.capabilities().completion_provider.is_some())
4939 .filter(|(adapter, _)| {
4940 scope
4941 .as_ref()
4942 .map(|scope| scope.language_allowed(&adapter.name))
4943 .unwrap_or(true)
4944 })
4945 .map(|(_, server)| server.server_id())
4946 .collect();
4947
4948 let buffer = buffer.clone();
4949 cx.spawn(move |this, mut cx| async move {
4950 let mut tasks = Vec::with_capacity(server_ids.len());
4951 this.update(&mut cx, |this, cx| {
4952 for server_id in server_ids {
4953 tasks.push(this.request_lsp(
4954 buffer.clone(),
4955 LanguageServerToQuery::Other(server_id),
4956 GetCompletions { position },
4957 cx,
4958 ));
4959 }
4960 })?;
4961
4962 let mut completions = Vec::new();
4963 for task in tasks {
4964 if let Ok(new_completions) = task.await {
4965 completions.extend_from_slice(&new_completions);
4966 }
4967 }
4968
4969 Ok(completions)
4970 })
4971 } else if let Some(project_id) = self.remote_id() {
4972 self.send_lsp_proto_request(buffer.clone(), project_id, GetCompletions { position }, cx)
4973 } else {
4974 Task::ready(Ok(Default::default()))
4975 }
4976 }
4977 pub fn completions<T: ToOffset + ToPointUtf16>(
4978 &self,
4979 buffer: &Model<Buffer>,
4980 position: T,
4981 cx: &mut ModelContext<Self>,
4982 ) -> Task<Result<Vec<Completion>>> {
4983 let position = position.to_point_utf16(buffer.read(cx));
4984 self.completions_impl(buffer, position, cx)
4985 }
4986
4987 pub fn resolve_completions(
4988 &self,
4989 completion_indices: Vec<usize>,
4990 completions: Arc<RwLock<Box<[Completion]>>>,
4991 cx: &mut ModelContext<Self>,
4992 ) -> Task<Result<bool>> {
4993 let client = self.client();
4994 let language_registry = self.languages().clone();
4995
4996 let is_remote = self.is_remote();
4997 let project_id = self.remote_id();
4998
4999 cx.spawn(move |this, mut cx| async move {
5000 let mut did_resolve = false;
5001 if is_remote {
5002 let project_id =
5003 project_id.ok_or_else(|| anyhow!("Remote project without remote_id"))?;
5004
5005 for completion_index in completion_indices {
5006 let completions_guard = completions.read();
5007 let completion = &completions_guard[completion_index];
5008 if completion.documentation.is_some() {
5009 continue;
5010 }
5011
5012 did_resolve = true;
5013 let server_id = completion.server_id;
5014 let completion = completion.lsp_completion.clone();
5015 drop(completions_guard);
5016
5017 Self::resolve_completion_documentation_remote(
5018 project_id,
5019 server_id,
5020 completions.clone(),
5021 completion_index,
5022 completion,
5023 client.clone(),
5024 language_registry.clone(),
5025 )
5026 .await;
5027 }
5028 } else {
5029 for completion_index in completion_indices {
5030 let completions_guard = completions.read();
5031 let completion = &completions_guard[completion_index];
5032 if completion.documentation.is_some() {
5033 continue;
5034 }
5035
5036 let server_id = completion.server_id;
5037 let completion = completion.lsp_completion.clone();
5038 drop(completions_guard);
5039
5040 let server = this
5041 .read_with(&mut cx, |project, _| {
5042 project.language_server_for_id(server_id)
5043 })
5044 .ok()
5045 .flatten();
5046 let Some(server) = server else {
5047 continue;
5048 };
5049
5050 did_resolve = true;
5051 Self::resolve_completion_documentation_local(
5052 server,
5053 completions.clone(),
5054 completion_index,
5055 completion,
5056 language_registry.clone(),
5057 )
5058 .await;
5059 }
5060 }
5061
5062 Ok(did_resolve)
5063 })
5064 }
5065
5066 async fn resolve_completion_documentation_local(
5067 server: Arc<lsp::LanguageServer>,
5068 completions: Arc<RwLock<Box<[Completion]>>>,
5069 completion_index: usize,
5070 completion: lsp::CompletionItem,
5071 language_registry: Arc<LanguageRegistry>,
5072 ) {
5073 let can_resolve = server
5074 .capabilities()
5075 .completion_provider
5076 .as_ref()
5077 .and_then(|options| options.resolve_provider)
5078 .unwrap_or(false);
5079 if !can_resolve {
5080 return;
5081 }
5082
5083 let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
5084 let Some(completion_item) = request.await.log_err() else {
5085 return;
5086 };
5087
5088 if let Some(lsp_documentation) = completion_item.documentation {
5089 let documentation = language::prepare_completion_documentation(
5090 &lsp_documentation,
5091 &language_registry,
5092 None, // TODO: Try to reasonably work out which language the completion is for
5093 )
5094 .await;
5095
5096 let mut completions = completions.write();
5097 let completion = &mut completions[completion_index];
5098 completion.documentation = Some(documentation);
5099 } else {
5100 let mut completions = completions.write();
5101 let completion = &mut completions[completion_index];
5102 completion.documentation = Some(Documentation::Undocumented);
5103 }
5104 }
5105
5106 async fn resolve_completion_documentation_remote(
5107 project_id: u64,
5108 server_id: LanguageServerId,
5109 completions: Arc<RwLock<Box<[Completion]>>>,
5110 completion_index: usize,
5111 completion: lsp::CompletionItem,
5112 client: Arc<Client>,
5113 language_registry: Arc<LanguageRegistry>,
5114 ) {
5115 let request = proto::ResolveCompletionDocumentation {
5116 project_id,
5117 language_server_id: server_id.0 as u64,
5118 lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
5119 };
5120
5121 let Some(response) = client
5122 .request(request)
5123 .await
5124 .context("completion documentation resolve proto request")
5125 .log_err()
5126 else {
5127 return;
5128 };
5129
5130 if response.text.is_empty() {
5131 let mut completions = completions.write();
5132 let completion = &mut completions[completion_index];
5133 completion.documentation = Some(Documentation::Undocumented);
5134 }
5135
5136 let documentation = if response.is_markdown {
5137 Documentation::MultiLineMarkdown(
5138 markdown::parse_markdown(&response.text, &language_registry, None).await,
5139 )
5140 } else if response.text.lines().count() <= 1 {
5141 Documentation::SingleLine(response.text)
5142 } else {
5143 Documentation::MultiLinePlainText(response.text)
5144 };
5145
5146 let mut completions = completions.write();
5147 let completion = &mut completions[completion_index];
5148 completion.documentation = Some(documentation);
5149 }
5150
5151 pub fn apply_additional_edits_for_completion(
5152 &self,
5153 buffer_handle: Model<Buffer>,
5154 completion: Completion,
5155 push_to_history: bool,
5156 cx: &mut ModelContext<Self>,
5157 ) -> Task<Result<Option<Transaction>>> {
5158 let buffer = buffer_handle.read(cx);
5159 let buffer_id = buffer.remote_id();
5160
5161 if self.is_local() {
5162 let server_id = completion.server_id;
5163 let lang_server = match self.language_server_for_buffer(buffer, server_id, cx) {
5164 Some((_, server)) => server.clone(),
5165 _ => return Task::ready(Ok(Default::default())),
5166 };
5167
5168 cx.spawn(move |this, mut cx| async move {
5169 let can_resolve = lang_server
5170 .capabilities()
5171 .completion_provider
5172 .as_ref()
5173 .and_then(|options| options.resolve_provider)
5174 .unwrap_or(false);
5175 let additional_text_edits = if can_resolve {
5176 lang_server
5177 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
5178 .await?
5179 .additional_text_edits
5180 } else {
5181 completion.lsp_completion.additional_text_edits
5182 };
5183 if let Some(edits) = additional_text_edits {
5184 let edits = this
5185 .update(&mut cx, |this, cx| {
5186 this.edits_from_lsp(
5187 &buffer_handle,
5188 edits,
5189 lang_server.server_id(),
5190 None,
5191 cx,
5192 )
5193 })?
5194 .await?;
5195
5196 buffer_handle.update(&mut cx, |buffer, cx| {
5197 buffer.finalize_last_transaction();
5198 buffer.start_transaction();
5199
5200 for (range, text) in edits {
5201 let primary = &completion.old_range;
5202 let start_within = primary.start.cmp(&range.start, buffer).is_le()
5203 && primary.end.cmp(&range.start, buffer).is_ge();
5204 let end_within = range.start.cmp(&primary.end, buffer).is_le()
5205 && range.end.cmp(&primary.end, buffer).is_ge();
5206
5207 //Skip additional edits which overlap with the primary completion edit
5208 //https://github.com/zed-industries/zed/pull/1871
5209 if !start_within && !end_within {
5210 buffer.edit([(range, text)], None, cx);
5211 }
5212 }
5213
5214 let transaction = if buffer.end_transaction(cx).is_some() {
5215 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5216 if !push_to_history {
5217 buffer.forget_transaction(transaction.id);
5218 }
5219 Some(transaction)
5220 } else {
5221 None
5222 };
5223 Ok(transaction)
5224 })?
5225 } else {
5226 Ok(None)
5227 }
5228 })
5229 } else if let Some(project_id) = self.remote_id() {
5230 let client = self.client.clone();
5231 cx.spawn(move |_, mut cx| async move {
5232 let response = client
5233 .request(proto::ApplyCompletionAdditionalEdits {
5234 project_id,
5235 buffer_id: buffer_id.into(),
5236 completion: Some(language::proto::serialize_completion(&completion)),
5237 })
5238 .await?;
5239
5240 if let Some(transaction) = response.transaction {
5241 let transaction = language::proto::deserialize_transaction(transaction)?;
5242 buffer_handle
5243 .update(&mut cx, |buffer, _| {
5244 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
5245 })?
5246 .await?;
5247 if push_to_history {
5248 buffer_handle.update(&mut cx, |buffer, _| {
5249 buffer.push_transaction(transaction.clone(), Instant::now());
5250 })?;
5251 }
5252 Ok(Some(transaction))
5253 } else {
5254 Ok(None)
5255 }
5256 })
5257 } else {
5258 Task::ready(Err(anyhow!("project does not have a remote id")))
5259 }
5260 }
5261
5262 fn code_actions_impl(
5263 &self,
5264 buffer_handle: &Model<Buffer>,
5265 range: Range<Anchor>,
5266 cx: &mut ModelContext<Self>,
5267 ) -> Task<Result<Vec<CodeAction>>> {
5268 self.request_lsp(
5269 buffer_handle.clone(),
5270 LanguageServerToQuery::Primary,
5271 GetCodeActions { range, kinds: None },
5272 cx,
5273 )
5274 }
5275
5276 pub fn code_actions<T: Clone + ToOffset>(
5277 &self,
5278 buffer_handle: &Model<Buffer>,
5279 range: Range<T>,
5280 cx: &mut ModelContext<Self>,
5281 ) -> Task<Result<Vec<CodeAction>>> {
5282 let buffer = buffer_handle.read(cx);
5283 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
5284 self.code_actions_impl(buffer_handle, range, cx)
5285 }
5286
5287 pub fn apply_code_action(
5288 &self,
5289 buffer_handle: Model<Buffer>,
5290 mut action: CodeAction,
5291 push_to_history: bool,
5292 cx: &mut ModelContext<Self>,
5293 ) -> Task<Result<ProjectTransaction>> {
5294 if self.is_local() {
5295 let buffer = buffer_handle.read(cx);
5296 let (lsp_adapter, lang_server) = if let Some((adapter, server)) =
5297 self.language_server_for_buffer(buffer, action.server_id, cx)
5298 {
5299 (adapter.clone(), server.clone())
5300 } else {
5301 return Task::ready(Ok(Default::default()));
5302 };
5303 let range = action.range.to_point_utf16(buffer);
5304
5305 cx.spawn(move |this, mut cx| async move {
5306 if let Some(lsp_range) = action
5307 .lsp_action
5308 .data
5309 .as_mut()
5310 .and_then(|d| d.get_mut("codeActionParams"))
5311 .and_then(|d| d.get_mut("range"))
5312 {
5313 *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
5314 action.lsp_action = lang_server
5315 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
5316 .await?;
5317 } else {
5318 let actions = this
5319 .update(&mut cx, |this, cx| {
5320 this.code_actions(&buffer_handle, action.range, cx)
5321 })?
5322 .await?;
5323 action.lsp_action = actions
5324 .into_iter()
5325 .find(|a| a.lsp_action.title == action.lsp_action.title)
5326 .ok_or_else(|| anyhow!("code action is outdated"))?
5327 .lsp_action;
5328 }
5329
5330 if let Some(edit) = action.lsp_action.edit {
5331 if edit.changes.is_some() || edit.document_changes.is_some() {
5332 return Self::deserialize_workspace_edit(
5333 this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
5334 edit,
5335 push_to_history,
5336 lsp_adapter.clone(),
5337 lang_server.clone(),
5338 &mut cx,
5339 )
5340 .await;
5341 }
5342 }
5343
5344 if let Some(command) = action.lsp_action.command {
5345 this.update(&mut cx, |this, _| {
5346 this.last_workspace_edits_by_language_server
5347 .remove(&lang_server.server_id());
5348 })?;
5349
5350 let result = lang_server
5351 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
5352 command: command.command,
5353 arguments: command.arguments.unwrap_or_default(),
5354 ..Default::default()
5355 })
5356 .await;
5357
5358 if let Err(err) = result {
5359 // TODO: LSP ERROR
5360 return Err(err);
5361 }
5362
5363 return Ok(this.update(&mut cx, |this, _| {
5364 this.last_workspace_edits_by_language_server
5365 .remove(&lang_server.server_id())
5366 .unwrap_or_default()
5367 })?);
5368 }
5369
5370 Ok(ProjectTransaction::default())
5371 })
5372 } else if let Some(project_id) = self.remote_id() {
5373 let client = self.client.clone();
5374 let request = proto::ApplyCodeAction {
5375 project_id,
5376 buffer_id: buffer_handle.read(cx).remote_id().into(),
5377 action: Some(language::proto::serialize_code_action(&action)),
5378 };
5379 cx.spawn(move |this, mut cx| async move {
5380 let response = client
5381 .request(request)
5382 .await?
5383 .transaction
5384 .ok_or_else(|| anyhow!("missing transaction"))?;
5385 this.update(&mut cx, |this, cx| {
5386 this.deserialize_project_transaction(response, push_to_history, cx)
5387 })?
5388 .await
5389 })
5390 } else {
5391 Task::ready(Err(anyhow!("project does not have a remote id")))
5392 }
5393 }
5394
5395 fn apply_on_type_formatting(
5396 &self,
5397 buffer: Model<Buffer>,
5398 position: Anchor,
5399 trigger: String,
5400 cx: &mut ModelContext<Self>,
5401 ) -> Task<Result<Option<Transaction>>> {
5402 if self.is_local() {
5403 cx.spawn(move |this, mut cx| async move {
5404 // Do not allow multiple concurrent formatting requests for the
5405 // same buffer.
5406 this.update(&mut cx, |this, cx| {
5407 this.buffers_being_formatted
5408 .insert(buffer.read(cx).remote_id())
5409 })?;
5410
5411 let _cleanup = defer({
5412 let this = this.clone();
5413 let mut cx = cx.clone();
5414 let closure_buffer = buffer.clone();
5415 move || {
5416 this.update(&mut cx, |this, cx| {
5417 this.buffers_being_formatted
5418 .remove(&closure_buffer.read(cx).remote_id());
5419 })
5420 .ok();
5421 }
5422 });
5423
5424 buffer
5425 .update(&mut cx, |buffer, _| {
5426 buffer.wait_for_edits(Some(position.timestamp))
5427 })?
5428 .await?;
5429 this.update(&mut cx, |this, cx| {
5430 let position = position.to_point_utf16(buffer.read(cx));
5431 this.on_type_format(buffer, position, trigger, false, cx)
5432 })?
5433 .await
5434 })
5435 } else if let Some(project_id) = self.remote_id() {
5436 let client = self.client.clone();
5437 let request = proto::OnTypeFormatting {
5438 project_id,
5439 buffer_id: buffer.read(cx).remote_id().into(),
5440 position: Some(serialize_anchor(&position)),
5441 trigger,
5442 version: serialize_version(&buffer.read(cx).version()),
5443 };
5444 cx.spawn(move |_, _| async move {
5445 client
5446 .request(request)
5447 .await?
5448 .transaction
5449 .map(language::proto::deserialize_transaction)
5450 .transpose()
5451 })
5452 } else {
5453 Task::ready(Err(anyhow!("project does not have a remote id")))
5454 }
5455 }
5456
5457 async fn deserialize_edits(
5458 this: Model<Self>,
5459 buffer_to_edit: Model<Buffer>,
5460 edits: Vec<lsp::TextEdit>,
5461 push_to_history: bool,
5462 _: Arc<CachedLspAdapter>,
5463 language_server: Arc<LanguageServer>,
5464 cx: &mut AsyncAppContext,
5465 ) -> Result<Option<Transaction>> {
5466 let edits = this
5467 .update(cx, |this, cx| {
5468 this.edits_from_lsp(
5469 &buffer_to_edit,
5470 edits,
5471 language_server.server_id(),
5472 None,
5473 cx,
5474 )
5475 })?
5476 .await?;
5477
5478 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
5479 buffer.finalize_last_transaction();
5480 buffer.start_transaction();
5481 for (range, text) in edits {
5482 buffer.edit([(range, text)], None, cx);
5483 }
5484
5485 if buffer.end_transaction(cx).is_some() {
5486 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5487 if !push_to_history {
5488 buffer.forget_transaction(transaction.id);
5489 }
5490 Some(transaction)
5491 } else {
5492 None
5493 }
5494 })?;
5495
5496 Ok(transaction)
5497 }
5498
5499 async fn deserialize_workspace_edit(
5500 this: Model<Self>,
5501 edit: lsp::WorkspaceEdit,
5502 push_to_history: bool,
5503 lsp_adapter: Arc<CachedLspAdapter>,
5504 language_server: Arc<LanguageServer>,
5505 cx: &mut AsyncAppContext,
5506 ) -> Result<ProjectTransaction> {
5507 let fs = this.update(cx, |this, _| this.fs.clone())?;
5508 let mut operations = Vec::new();
5509 if let Some(document_changes) = edit.document_changes {
5510 match document_changes {
5511 lsp::DocumentChanges::Edits(edits) => {
5512 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
5513 }
5514 lsp::DocumentChanges::Operations(ops) => operations = ops,
5515 }
5516 } else if let Some(changes) = edit.changes {
5517 operations.extend(changes.into_iter().map(|(uri, edits)| {
5518 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
5519 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
5520 uri,
5521 version: None,
5522 },
5523 edits: edits.into_iter().map(OneOf::Left).collect(),
5524 })
5525 }));
5526 }
5527
5528 let mut project_transaction = ProjectTransaction::default();
5529 for operation in operations {
5530 match operation {
5531 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
5532 let abs_path = op
5533 .uri
5534 .to_file_path()
5535 .map_err(|_| anyhow!("can't convert URI to path"))?;
5536
5537 if let Some(parent_path) = abs_path.parent() {
5538 fs.create_dir(parent_path).await?;
5539 }
5540 if abs_path.ends_with("/") {
5541 fs.create_dir(&abs_path).await?;
5542 } else {
5543 fs.create_file(
5544 &abs_path,
5545 op.options
5546 .map(|options| fs::CreateOptions {
5547 overwrite: options.overwrite.unwrap_or(false),
5548 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
5549 })
5550 .unwrap_or_default(),
5551 )
5552 .await?;
5553 }
5554 }
5555
5556 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
5557 let source_abs_path = op
5558 .old_uri
5559 .to_file_path()
5560 .map_err(|_| anyhow!("can't convert URI to path"))?;
5561 let target_abs_path = op
5562 .new_uri
5563 .to_file_path()
5564 .map_err(|_| anyhow!("can't convert URI to path"))?;
5565 fs.rename(
5566 &source_abs_path,
5567 &target_abs_path,
5568 op.options
5569 .map(|options| fs::RenameOptions {
5570 overwrite: options.overwrite.unwrap_or(false),
5571 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
5572 })
5573 .unwrap_or_default(),
5574 )
5575 .await?;
5576 }
5577
5578 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
5579 let abs_path = op
5580 .uri
5581 .to_file_path()
5582 .map_err(|_| anyhow!("can't convert URI to path"))?;
5583 let options = op
5584 .options
5585 .map(|options| fs::RemoveOptions {
5586 recursive: options.recursive.unwrap_or(false),
5587 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
5588 })
5589 .unwrap_or_default();
5590 if abs_path.ends_with("/") {
5591 fs.remove_dir(&abs_path, options).await?;
5592 } else {
5593 fs.remove_file(&abs_path, options).await?;
5594 }
5595 }
5596
5597 lsp::DocumentChangeOperation::Edit(op) => {
5598 let buffer_to_edit = this
5599 .update(cx, |this, cx| {
5600 this.open_local_buffer_via_lsp(
5601 op.text_document.uri,
5602 language_server.server_id(),
5603 lsp_adapter.name.clone(),
5604 cx,
5605 )
5606 })?
5607 .await?;
5608
5609 let edits = this
5610 .update(cx, |this, cx| {
5611 let edits = op.edits.into_iter().map(|edit| match edit {
5612 OneOf::Left(edit) => edit,
5613 OneOf::Right(edit) => edit.text_edit,
5614 });
5615 this.edits_from_lsp(
5616 &buffer_to_edit,
5617 edits,
5618 language_server.server_id(),
5619 op.text_document.version,
5620 cx,
5621 )
5622 })?
5623 .await?;
5624
5625 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
5626 buffer.finalize_last_transaction();
5627 buffer.start_transaction();
5628 for (range, text) in edits {
5629 buffer.edit([(range, text)], None, cx);
5630 }
5631 let transaction = if buffer.end_transaction(cx).is_some() {
5632 let transaction = buffer.finalize_last_transaction().unwrap().clone();
5633 if !push_to_history {
5634 buffer.forget_transaction(transaction.id);
5635 }
5636 Some(transaction)
5637 } else {
5638 None
5639 };
5640
5641 transaction
5642 })?;
5643 if let Some(transaction) = transaction {
5644 project_transaction.0.insert(buffer_to_edit, transaction);
5645 }
5646 }
5647 }
5648 }
5649
5650 Ok(project_transaction)
5651 }
5652
5653 fn prepare_rename_impl(
5654 &self,
5655 buffer: Model<Buffer>,
5656 position: PointUtf16,
5657 cx: &mut ModelContext<Self>,
5658 ) -> Task<Result<Option<Range<Anchor>>>> {
5659 self.request_lsp(
5660 buffer,
5661 LanguageServerToQuery::Primary,
5662 PrepareRename { position },
5663 cx,
5664 )
5665 }
5666 pub fn prepare_rename<T: ToPointUtf16>(
5667 &self,
5668 buffer: Model<Buffer>,
5669 position: T,
5670 cx: &mut ModelContext<Self>,
5671 ) -> Task<Result<Option<Range<Anchor>>>> {
5672 let position = position.to_point_utf16(buffer.read(cx));
5673 self.prepare_rename_impl(buffer, position, cx)
5674 }
5675
5676 fn perform_rename_impl(
5677 &self,
5678 buffer: Model<Buffer>,
5679 position: PointUtf16,
5680 new_name: String,
5681 push_to_history: bool,
5682 cx: &mut ModelContext<Self>,
5683 ) -> Task<Result<ProjectTransaction>> {
5684 let position = position.to_point_utf16(buffer.read(cx));
5685 self.request_lsp(
5686 buffer,
5687 LanguageServerToQuery::Primary,
5688 PerformRename {
5689 position,
5690 new_name,
5691 push_to_history,
5692 },
5693 cx,
5694 )
5695 }
5696 pub fn perform_rename<T: ToPointUtf16>(
5697 &self,
5698 buffer: Model<Buffer>,
5699 position: T,
5700 new_name: String,
5701 push_to_history: bool,
5702 cx: &mut ModelContext<Self>,
5703 ) -> Task<Result<ProjectTransaction>> {
5704 let position = position.to_point_utf16(buffer.read(cx));
5705 self.perform_rename_impl(buffer, position, new_name, push_to_history, cx)
5706 }
5707
5708 pub fn on_type_format_impl(
5709 &self,
5710 buffer: Model<Buffer>,
5711 position: PointUtf16,
5712 trigger: String,
5713 push_to_history: bool,
5714 cx: &mut ModelContext<Self>,
5715 ) -> Task<Result<Option<Transaction>>> {
5716 let tab_size = buffer.update(cx, |buffer, cx| {
5717 language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx).tab_size
5718 });
5719 self.request_lsp(
5720 buffer.clone(),
5721 LanguageServerToQuery::Primary,
5722 OnTypeFormatting {
5723 position,
5724 trigger,
5725 options: lsp_command::lsp_formatting_options(tab_size.get()).into(),
5726 push_to_history,
5727 },
5728 cx,
5729 )
5730 }
5731
5732 pub fn on_type_format<T: ToPointUtf16>(
5733 &self,
5734 buffer: Model<Buffer>,
5735 position: T,
5736 trigger: String,
5737 push_to_history: bool,
5738 cx: &mut ModelContext<Self>,
5739 ) -> Task<Result<Option<Transaction>>> {
5740 let position = position.to_point_utf16(buffer.read(cx));
5741 self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
5742 }
5743
5744 pub fn inlay_hints<T: ToOffset>(
5745 &self,
5746 buffer_handle: Model<Buffer>,
5747 range: Range<T>,
5748 cx: &mut ModelContext<Self>,
5749 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
5750 let buffer = buffer_handle.read(cx);
5751 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
5752 self.inlay_hints_impl(buffer_handle, range, cx)
5753 }
5754 fn inlay_hints_impl(
5755 &self,
5756 buffer_handle: Model<Buffer>,
5757 range: Range<Anchor>,
5758 cx: &mut ModelContext<Self>,
5759 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
5760 let buffer = buffer_handle.read(cx);
5761 let range_start = range.start;
5762 let range_end = range.end;
5763 let buffer_id = buffer.remote_id().into();
5764 let lsp_request = InlayHints { range };
5765
5766 if self.is_local() {
5767 let lsp_request_task = self.request_lsp(
5768 buffer_handle.clone(),
5769 LanguageServerToQuery::Primary,
5770 lsp_request,
5771 cx,
5772 );
5773 cx.spawn(move |_, mut cx| async move {
5774 buffer_handle
5775 .update(&mut cx, |buffer, _| {
5776 buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
5777 })?
5778 .await
5779 .context("waiting for inlay hint request range edits")?;
5780 lsp_request_task.await.context("inlay hints LSP request")
5781 })
5782 } else if let Some(project_id) = self.remote_id() {
5783 let client = self.client.clone();
5784 let request = proto::InlayHints {
5785 project_id,
5786 buffer_id,
5787 start: Some(serialize_anchor(&range_start)),
5788 end: Some(serialize_anchor(&range_end)),
5789 version: serialize_version(&buffer_handle.read(cx).version()),
5790 };
5791 cx.spawn(move |project, cx| async move {
5792 let response = client
5793 .request(request)
5794 .await
5795 .context("inlay hints proto request")?;
5796 LspCommand::response_from_proto(
5797 lsp_request,
5798 response,
5799 project.upgrade().ok_or_else(|| anyhow!("No project"))?,
5800 buffer_handle.clone(),
5801 cx.clone(),
5802 )
5803 .await
5804 .context("inlay hints proto response conversion")
5805 })
5806 } else {
5807 Task::ready(Err(anyhow!("project does not have a remote id")))
5808 }
5809 }
5810
5811 pub fn resolve_inlay_hint(
5812 &self,
5813 hint: InlayHint,
5814 buffer_handle: Model<Buffer>,
5815 server_id: LanguageServerId,
5816 cx: &mut ModelContext<Self>,
5817 ) -> Task<anyhow::Result<InlayHint>> {
5818 if self.is_local() {
5819 let buffer = buffer_handle.read(cx);
5820 let (_, lang_server) = if let Some((adapter, server)) =
5821 self.language_server_for_buffer(buffer, server_id, cx)
5822 {
5823 (adapter.clone(), server.clone())
5824 } else {
5825 return Task::ready(Ok(hint));
5826 };
5827 if !InlayHints::can_resolve_inlays(lang_server.capabilities()) {
5828 return Task::ready(Ok(hint));
5829 }
5830
5831 let buffer_snapshot = buffer.snapshot();
5832 cx.spawn(move |_, mut cx| async move {
5833 let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
5834 InlayHints::project_to_lsp_hint(hint, &buffer_snapshot),
5835 );
5836 let resolved_hint = resolve_task
5837 .await
5838 .context("inlay hint resolve LSP request")?;
5839 let resolved_hint = InlayHints::lsp_to_project_hint(
5840 resolved_hint,
5841 &buffer_handle,
5842 server_id,
5843 ResolveState::Resolved,
5844 false,
5845 &mut cx,
5846 )
5847 .await?;
5848 Ok(resolved_hint)
5849 })
5850 } else if let Some(project_id) = self.remote_id() {
5851 let client = self.client.clone();
5852 let request = proto::ResolveInlayHint {
5853 project_id,
5854 buffer_id: buffer_handle.read(cx).remote_id().into(),
5855 language_server_id: server_id.0 as u64,
5856 hint: Some(InlayHints::project_to_proto_hint(hint.clone())),
5857 };
5858 cx.spawn(move |_, _| async move {
5859 let response = client
5860 .request(request)
5861 .await
5862 .context("inlay hints proto request")?;
5863 match response.hint {
5864 Some(resolved_hint) => InlayHints::proto_to_project_hint(resolved_hint)
5865 .context("inlay hints proto resolve response conversion"),
5866 None => Ok(hint),
5867 }
5868 })
5869 } else {
5870 Task::ready(Err(anyhow!("project does not have a remote id")))
5871 }
5872 }
5873
5874 #[allow(clippy::type_complexity)]
5875 pub fn search(
5876 &self,
5877 query: SearchQuery,
5878 cx: &mut ModelContext<Self>,
5879 ) -> Receiver<(Model<Buffer>, Vec<Range<Anchor>>)> {
5880 if self.is_local() {
5881 self.search_local(query, cx)
5882 } else if let Some(project_id) = self.remote_id() {
5883 let (tx, rx) = smol::channel::unbounded();
5884 let request = self.client.request(query.to_proto(project_id));
5885 cx.spawn(move |this, mut cx| async move {
5886 let response = request.await?;
5887 let mut result = HashMap::default();
5888 for location in response.locations {
5889 let buffer_id = BufferId::new(location.buffer_id)?;
5890 let target_buffer = this
5891 .update(&mut cx, |this, cx| {
5892 this.wait_for_remote_buffer(buffer_id, cx)
5893 })?
5894 .await?;
5895 let start = location
5896 .start
5897 .and_then(deserialize_anchor)
5898 .ok_or_else(|| anyhow!("missing target start"))?;
5899 let end = location
5900 .end
5901 .and_then(deserialize_anchor)
5902 .ok_or_else(|| anyhow!("missing target end"))?;
5903 result
5904 .entry(target_buffer)
5905 .or_insert(Vec::new())
5906 .push(start..end)
5907 }
5908 for (buffer, ranges) in result {
5909 let _ = tx.send((buffer, ranges)).await;
5910 }
5911 Result::<(), anyhow::Error>::Ok(())
5912 })
5913 .detach_and_log_err(cx);
5914 rx
5915 } else {
5916 unimplemented!();
5917 }
5918 }
5919
5920 pub fn search_local(
5921 &self,
5922 query: SearchQuery,
5923 cx: &mut ModelContext<Self>,
5924 ) -> Receiver<(Model<Buffer>, Vec<Range<Anchor>>)> {
5925 // Local search is split into several phases.
5926 // TL;DR is that we do 2 passes; initial pass to pick files which contain at least one match
5927 // and the second phase that finds positions of all the matches found in the candidate files.
5928 // The Receiver obtained from this function returns matches sorted by buffer path. Files without a buffer path are reported first.
5929 //
5930 // It gets a bit hairy though, because we must account for files that do not have a persistent representation
5931 // on FS. Namely, if you have an untitled buffer or unsaved changes in a buffer, we want to scan that too.
5932 //
5933 // 1. We initialize a queue of match candidates and feed all opened buffers into it (== unsaved files / untitled buffers).
5934 // Then, we go through a worktree and check for files that do match a predicate. If the file had an opened version, we skip the scan
5935 // of FS version for that file altogether - after all, what we have in memory is more up-to-date than what's in FS.
5936 // 2. At this point, we have a list of all potentially matching buffers/files.
5937 // We sort that list by buffer path - this list is retained for later use.
5938 // We ensure that all buffers are now opened and available in project.
5939 // 3. We run a scan over all the candidate buffers on multiple background threads.
5940 // We cannot assume that there will even be a match - while at least one match
5941 // is guaranteed for files obtained from FS, the buffers we got from memory (unsaved files/unnamed buffers) might not have a match at all.
5942 // There is also an auxiliary background thread responsible for result gathering.
5943 // This is where the sorted list of buffers comes into play to maintain sorted order; Whenever this background thread receives a notification (buffer has/doesn't have matches),
5944 // it keeps it around. It reports matches in sorted order, though it accepts them in unsorted order as well.
5945 // As soon as the match info on next position in sorted order becomes available, it reports it (if it's a match) or skips to the next
5946 // entry - which might already be available thanks to out-of-order processing.
5947 //
5948 // We could also report matches fully out-of-order, without maintaining a sorted list of matching paths.
5949 // This however would mean that project search (that is the main user of this function) would have to do the sorting itself, on the go.
5950 // This isn't as straightforward as running an insertion sort sadly, and would also mean that it would have to care about maintaining match index
5951 // in face of constantly updating list of sorted matches.
5952 // Meanwhile, this implementation offers index stability, since the matches are already reported in a sorted order.
5953 let snapshots = self
5954 .visible_worktrees(cx)
5955 .filter_map(|tree| {
5956 let tree = tree.read(cx).as_local()?;
5957 Some(tree.snapshot())
5958 })
5959 .collect::<Vec<_>>();
5960
5961 let background = cx.background_executor().clone();
5962 let path_count: usize = snapshots
5963 .iter()
5964 .map(|s| {
5965 if query.include_ignored() {
5966 s.file_count()
5967 } else {
5968 s.visible_file_count()
5969 }
5970 })
5971 .sum();
5972 if path_count == 0 {
5973 let (_, rx) = smol::channel::bounded(1024);
5974 return rx;
5975 }
5976 let workers = background.num_cpus().min(path_count);
5977 let (matching_paths_tx, matching_paths_rx) = smol::channel::bounded(1024);
5978 let mut unnamed_files = vec![];
5979 let opened_buffers = self
5980 .opened_buffers
5981 .iter()
5982 .filter_map(|(_, b)| {
5983 let buffer = b.upgrade()?;
5984 let (is_ignored, snapshot) = buffer.update(cx, |buffer, cx| {
5985 let is_ignored = buffer
5986 .project_path(cx)
5987 .and_then(|path| self.entry_for_path(&path, cx))
5988 .map_or(false, |entry| entry.is_ignored);
5989 (is_ignored, buffer.snapshot())
5990 });
5991 if is_ignored && !query.include_ignored() {
5992 return None;
5993 } else if let Some(path) = snapshot.file().map(|file| file.path()) {
5994 Some((path.clone(), (buffer, snapshot)))
5995 } else {
5996 unnamed_files.push(buffer);
5997 None
5998 }
5999 })
6000 .collect();
6001 cx.background_executor()
6002 .spawn(Self::background_search(
6003 unnamed_files,
6004 opened_buffers,
6005 cx.background_executor().clone(),
6006 self.fs.clone(),
6007 workers,
6008 query.clone(),
6009 path_count,
6010 snapshots,
6011 matching_paths_tx,
6012 ))
6013 .detach();
6014
6015 let (buffers, buffers_rx) = Self::sort_candidates_and_open_buffers(matching_paths_rx, cx);
6016 let background = cx.background_executor().clone();
6017 let (result_tx, result_rx) = smol::channel::bounded(1024);
6018 cx.background_executor()
6019 .spawn(async move {
6020 let Ok(buffers) = buffers.await else {
6021 return;
6022 };
6023
6024 let buffers_len = buffers.len();
6025 if buffers_len == 0 {
6026 return;
6027 }
6028 let query = &query;
6029 let (finished_tx, mut finished_rx) = smol::channel::unbounded();
6030 background
6031 .scoped(|scope| {
6032 #[derive(Clone)]
6033 struct FinishedStatus {
6034 entry: Option<(Model<Buffer>, Vec<Range<Anchor>>)>,
6035 buffer_index: SearchMatchCandidateIndex,
6036 }
6037
6038 for _ in 0..workers {
6039 let finished_tx = finished_tx.clone();
6040 let mut buffers_rx = buffers_rx.clone();
6041 scope.spawn(async move {
6042 while let Some((entry, buffer_index)) = buffers_rx.next().await {
6043 let buffer_matches = if let Some((_, snapshot)) = entry.as_ref()
6044 {
6045 if query.file_matches(
6046 snapshot.file().map(|file| file.path().as_ref()),
6047 ) {
6048 query
6049 .search(snapshot, None)
6050 .await
6051 .iter()
6052 .map(|range| {
6053 snapshot.anchor_before(range.start)
6054 ..snapshot.anchor_after(range.end)
6055 })
6056 .collect()
6057 } else {
6058 Vec::new()
6059 }
6060 } else {
6061 Vec::new()
6062 };
6063
6064 let status = if !buffer_matches.is_empty() {
6065 let entry = if let Some((buffer, _)) = entry.as_ref() {
6066 Some((buffer.clone(), buffer_matches))
6067 } else {
6068 None
6069 };
6070 FinishedStatus {
6071 entry,
6072 buffer_index,
6073 }
6074 } else {
6075 FinishedStatus {
6076 entry: None,
6077 buffer_index,
6078 }
6079 };
6080 if finished_tx.send(status).await.is_err() {
6081 break;
6082 }
6083 }
6084 });
6085 }
6086 // Report sorted matches
6087 scope.spawn(async move {
6088 let mut current_index = 0;
6089 let mut scratch = vec![None; buffers_len];
6090 while let Some(status) = finished_rx.next().await {
6091 debug_assert!(
6092 scratch[status.buffer_index].is_none(),
6093 "Got match status of position {} twice",
6094 status.buffer_index
6095 );
6096 let index = status.buffer_index;
6097 scratch[index] = Some(status);
6098 while current_index < buffers_len {
6099 let Some(current_entry) = scratch[current_index].take() else {
6100 // We intentionally **do not** increment `current_index` here. When next element arrives
6101 // from `finished_rx`, we will inspect the same position again, hoping for it to be Some(_)
6102 // this time.
6103 break;
6104 };
6105 if let Some(entry) = current_entry.entry {
6106 result_tx.send(entry).await.log_err();
6107 }
6108 current_index += 1;
6109 }
6110 if current_index == buffers_len {
6111 break;
6112 }
6113 }
6114 });
6115 })
6116 .await;
6117 })
6118 .detach();
6119 result_rx
6120 }
6121
6122 /// Pick paths that might potentially contain a match of a given search query.
6123 async fn background_search(
6124 unnamed_buffers: Vec<Model<Buffer>>,
6125 opened_buffers: HashMap<Arc<Path>, (Model<Buffer>, BufferSnapshot)>,
6126 executor: BackgroundExecutor,
6127 fs: Arc<dyn Fs>,
6128 workers: usize,
6129 query: SearchQuery,
6130 path_count: usize,
6131 snapshots: Vec<LocalSnapshot>,
6132 matching_paths_tx: Sender<SearchMatchCandidate>,
6133 ) {
6134 let fs = &fs;
6135 let query = &query;
6136 let matching_paths_tx = &matching_paths_tx;
6137 let snapshots = &snapshots;
6138 let paths_per_worker = (path_count + workers - 1) / workers;
6139 for buffer in unnamed_buffers {
6140 matching_paths_tx
6141 .send(SearchMatchCandidate::OpenBuffer {
6142 buffer: buffer.clone(),
6143 path: None,
6144 })
6145 .await
6146 .log_err();
6147 }
6148 for (path, (buffer, _)) in opened_buffers.iter() {
6149 matching_paths_tx
6150 .send(SearchMatchCandidate::OpenBuffer {
6151 buffer: buffer.clone(),
6152 path: Some(path.clone()),
6153 })
6154 .await
6155 .log_err();
6156 }
6157 executor
6158 .scoped(|scope| {
6159 let max_concurrent_workers = Arc::new(Semaphore::new(workers));
6160
6161 for worker_ix in 0..workers {
6162 let worker_start_ix = worker_ix * paths_per_worker;
6163 let worker_end_ix = worker_start_ix + paths_per_worker;
6164 let unnamed_buffers = opened_buffers.clone();
6165 let limiter = Arc::clone(&max_concurrent_workers);
6166 scope.spawn(async move {
6167 let _guard = limiter.acquire().await;
6168 let mut snapshot_start_ix = 0;
6169 let mut abs_path = PathBuf::new();
6170 for snapshot in snapshots {
6171 let snapshot_end_ix = snapshot_start_ix
6172 + if query.include_ignored() {
6173 snapshot.file_count()
6174 } else {
6175 snapshot.visible_file_count()
6176 };
6177 if worker_end_ix <= snapshot_start_ix {
6178 break;
6179 } else if worker_start_ix > snapshot_end_ix {
6180 snapshot_start_ix = snapshot_end_ix;
6181 continue;
6182 } else {
6183 let start_in_snapshot =
6184 worker_start_ix.saturating_sub(snapshot_start_ix);
6185 let end_in_snapshot =
6186 cmp::min(worker_end_ix, snapshot_end_ix) - snapshot_start_ix;
6187
6188 for entry in snapshot
6189 .files(query.include_ignored(), start_in_snapshot)
6190 .take(end_in_snapshot - start_in_snapshot)
6191 {
6192 if matching_paths_tx.is_closed() {
6193 break;
6194 }
6195 if unnamed_buffers.contains_key(&entry.path) {
6196 continue;
6197 }
6198 let matches = if query.file_matches(Some(&entry.path)) {
6199 abs_path.clear();
6200 abs_path.push(&snapshot.abs_path());
6201 abs_path.push(&entry.path);
6202 if let Some(file) = fs.open_sync(&abs_path).await.log_err()
6203 {
6204 query.detect(file).unwrap_or(false)
6205 } else {
6206 false
6207 }
6208 } else {
6209 false
6210 };
6211
6212 if matches {
6213 let project_path = SearchMatchCandidate::Path {
6214 worktree_id: snapshot.id(),
6215 path: entry.path.clone(),
6216 is_ignored: entry.is_ignored,
6217 };
6218 if matching_paths_tx.send(project_path).await.is_err() {
6219 break;
6220 }
6221 }
6222 }
6223
6224 snapshot_start_ix = snapshot_end_ix;
6225 }
6226 }
6227 });
6228 }
6229
6230 if query.include_ignored() {
6231 for snapshot in snapshots {
6232 for ignored_entry in snapshot
6233 .entries(query.include_ignored())
6234 .filter(|e| e.is_ignored)
6235 {
6236 let limiter = Arc::clone(&max_concurrent_workers);
6237 scope.spawn(async move {
6238 let _guard = limiter.acquire().await;
6239 let mut ignored_paths_to_process =
6240 VecDeque::from([snapshot.abs_path().join(&ignored_entry.path)]);
6241 while let Some(ignored_abs_path) =
6242 ignored_paths_to_process.pop_front()
6243 {
6244 if let Some(fs_metadata) = fs
6245 .metadata(&ignored_abs_path)
6246 .await
6247 .with_context(|| {
6248 format!("fetching fs metadata for {ignored_abs_path:?}")
6249 })
6250 .log_err()
6251 .flatten()
6252 {
6253 if fs_metadata.is_dir {
6254 if let Some(mut subfiles) = fs
6255 .read_dir(&ignored_abs_path)
6256 .await
6257 .with_context(|| {
6258 format!(
6259 "listing ignored path {ignored_abs_path:?}"
6260 )
6261 })
6262 .log_err()
6263 {
6264 while let Some(subfile) = subfiles.next().await {
6265 if let Some(subfile) = subfile.log_err() {
6266 ignored_paths_to_process.push_back(subfile);
6267 }
6268 }
6269 }
6270 } else if !fs_metadata.is_symlink {
6271 if !query.file_matches(Some(&ignored_abs_path))
6272 || snapshot.is_path_excluded(
6273 ignored_entry.path.to_path_buf(),
6274 )
6275 {
6276 continue;
6277 }
6278 let matches = if let Some(file) = fs
6279 .open_sync(&ignored_abs_path)
6280 .await
6281 .with_context(|| {
6282 format!(
6283 "Opening ignored path {ignored_abs_path:?}"
6284 )
6285 })
6286 .log_err()
6287 {
6288 query.detect(file).unwrap_or(false)
6289 } else {
6290 false
6291 };
6292 if matches {
6293 let project_path = SearchMatchCandidate::Path {
6294 worktree_id: snapshot.id(),
6295 path: Arc::from(
6296 ignored_abs_path
6297 .strip_prefix(snapshot.abs_path())
6298 .expect(
6299 "scanning worktree-related files",
6300 ),
6301 ),
6302 is_ignored: true,
6303 };
6304 if matching_paths_tx
6305 .send(project_path)
6306 .await
6307 .is_err()
6308 {
6309 return;
6310 }
6311 }
6312 }
6313 }
6314 }
6315 });
6316 }
6317 }
6318 }
6319 })
6320 .await;
6321 }
6322
6323 pub fn request_lsp<R: LspCommand>(
6324 &self,
6325 buffer_handle: Model<Buffer>,
6326 server: LanguageServerToQuery,
6327 request: R,
6328 cx: &mut ModelContext<Self>,
6329 ) -> Task<Result<R::Response>>
6330 where
6331 <R::LspRequest as lsp::request::Request>::Result: Send,
6332 <R::LspRequest as lsp::request::Request>::Params: Send,
6333 {
6334 let buffer = buffer_handle.read(cx);
6335 if self.is_local() {
6336 let language_server = match server {
6337 LanguageServerToQuery::Primary => {
6338 match self.primary_language_server_for_buffer(buffer, cx) {
6339 Some((_, server)) => Some(Arc::clone(server)),
6340 None => return Task::ready(Ok(Default::default())),
6341 }
6342 }
6343 LanguageServerToQuery::Other(id) => self
6344 .language_server_for_buffer(buffer, id, cx)
6345 .map(|(_, server)| Arc::clone(server)),
6346 };
6347 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
6348 if let (Some(file), Some(language_server)) = (file, language_server) {
6349 let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx);
6350 return cx.spawn(move |this, cx| async move {
6351 if !request.check_capabilities(language_server.capabilities()) {
6352 return Ok(Default::default());
6353 }
6354
6355 let result = language_server.request::<R::LspRequest>(lsp_params).await;
6356 let response = match result {
6357 Ok(response) => response,
6358
6359 Err(err) => {
6360 log::warn!(
6361 "Generic lsp request to {} failed: {}",
6362 language_server.name(),
6363 err
6364 );
6365 return Err(err);
6366 }
6367 };
6368
6369 request
6370 .response_from_lsp(
6371 response,
6372 this.upgrade().ok_or_else(|| anyhow!("no app context"))?,
6373 buffer_handle,
6374 language_server.server_id(),
6375 cx,
6376 )
6377 .await
6378 });
6379 }
6380 } else if let Some(project_id) = self.remote_id() {
6381 return self.send_lsp_proto_request(buffer_handle, project_id, request, cx);
6382 }
6383
6384 Task::ready(Ok(Default::default()))
6385 }
6386
6387 fn send_lsp_proto_request<R: LspCommand>(
6388 &self,
6389 buffer: Model<Buffer>,
6390 project_id: u64,
6391 request: R,
6392 cx: &mut ModelContext<'_, Project>,
6393 ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
6394 let rpc = self.client.clone();
6395 let message = request.to_proto(project_id, buffer.read(cx));
6396 cx.spawn(move |this, mut cx| async move {
6397 // Ensure the project is still alive by the time the task
6398 // is scheduled.
6399 this.upgrade().context("project dropped")?;
6400 let response = rpc.request(message).await?;
6401 let this = this.upgrade().context("project dropped")?;
6402 if this.update(&mut cx, |this, _| this.is_disconnected())? {
6403 Err(anyhow!("disconnected before completing request"))
6404 } else {
6405 request
6406 .response_from_proto(response, this, buffer, cx)
6407 .await
6408 }
6409 })
6410 }
6411
6412 fn sort_candidates_and_open_buffers(
6413 mut matching_paths_rx: Receiver<SearchMatchCandidate>,
6414 cx: &mut ModelContext<Self>,
6415 ) -> (
6416 futures::channel::oneshot::Receiver<Vec<SearchMatchCandidate>>,
6417 Receiver<(
6418 Option<(Model<Buffer>, BufferSnapshot)>,
6419 SearchMatchCandidateIndex,
6420 )>,
6421 ) {
6422 let (buffers_tx, buffers_rx) = smol::channel::bounded(1024);
6423 let (sorted_buffers_tx, sorted_buffers_rx) = futures::channel::oneshot::channel();
6424 cx.spawn(move |this, cx| async move {
6425 let mut buffers = Vec::new();
6426 let mut ignored_buffers = Vec::new();
6427 while let Some(entry) = matching_paths_rx.next().await {
6428 if matches!(
6429 entry,
6430 SearchMatchCandidate::Path {
6431 is_ignored: true,
6432 ..
6433 }
6434 ) {
6435 ignored_buffers.push(entry);
6436 } else {
6437 buffers.push(entry);
6438 }
6439 }
6440 buffers.sort_by_key(|candidate| candidate.path());
6441 ignored_buffers.sort_by_key(|candidate| candidate.path());
6442 buffers.extend(ignored_buffers);
6443 let matching_paths = buffers.clone();
6444 let _ = sorted_buffers_tx.send(buffers);
6445 for (index, candidate) in matching_paths.into_iter().enumerate() {
6446 if buffers_tx.is_closed() {
6447 break;
6448 }
6449 let this = this.clone();
6450 let buffers_tx = buffers_tx.clone();
6451 cx.spawn(move |mut cx| async move {
6452 let buffer = match candidate {
6453 SearchMatchCandidate::OpenBuffer { buffer, .. } => Some(buffer),
6454 SearchMatchCandidate::Path {
6455 worktree_id, path, ..
6456 } => this
6457 .update(&mut cx, |this, cx| {
6458 this.open_buffer((worktree_id, path), cx)
6459 })?
6460 .await
6461 .log_err(),
6462 };
6463 if let Some(buffer) = buffer {
6464 let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
6465 buffers_tx
6466 .send((Some((buffer, snapshot)), index))
6467 .await
6468 .log_err();
6469 } else {
6470 buffers_tx.send((None, index)).await.log_err();
6471 }
6472
6473 Ok::<_, anyhow::Error>(())
6474 })
6475 .detach();
6476 }
6477 })
6478 .detach();
6479 (sorted_buffers_rx, buffers_rx)
6480 }
6481
6482 pub fn find_or_create_local_worktree(
6483 &mut self,
6484 abs_path: impl AsRef<Path>,
6485 visible: bool,
6486 cx: &mut ModelContext<Self>,
6487 ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
6488 let abs_path = abs_path.as_ref();
6489 if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
6490 Task::ready(Ok((tree, relative_path)))
6491 } else {
6492 let worktree = self.create_local_worktree(abs_path, visible, cx);
6493 cx.background_executor()
6494 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
6495 }
6496 }
6497
6498 pub fn find_local_worktree(
6499 &self,
6500 abs_path: &Path,
6501 cx: &AppContext,
6502 ) -> Option<(Model<Worktree>, PathBuf)> {
6503 for tree in &self.worktrees {
6504 if let Some(tree) = tree.upgrade() {
6505 if let Some(relative_path) = tree
6506 .read(cx)
6507 .as_local()
6508 .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
6509 {
6510 return Some((tree.clone(), relative_path.into()));
6511 }
6512 }
6513 }
6514 None
6515 }
6516
6517 pub fn is_shared(&self) -> bool {
6518 match &self.client_state {
6519 ProjectClientState::Shared { .. } => true,
6520 ProjectClientState::Local | ProjectClientState::Remote { .. } => false,
6521 }
6522 }
6523
6524 fn create_local_worktree(
6525 &mut self,
6526 abs_path: impl AsRef<Path>,
6527 visible: bool,
6528 cx: &mut ModelContext<Self>,
6529 ) -> Task<Result<Model<Worktree>>> {
6530 let fs = self.fs.clone();
6531 let client = self.client.clone();
6532 let next_entry_id = self.next_entry_id.clone();
6533 let path: Arc<Path> = abs_path.as_ref().into();
6534 let task = self
6535 .loading_local_worktrees
6536 .entry(path.clone())
6537 .or_insert_with(|| {
6538 cx.spawn(move |project, mut cx| {
6539 async move {
6540 let worktree = Worktree::local(
6541 client.clone(),
6542 path.clone(),
6543 visible,
6544 fs,
6545 next_entry_id,
6546 &mut cx,
6547 )
6548 .await;
6549
6550 project.update(&mut cx, |project, _| {
6551 project.loading_local_worktrees.remove(&path);
6552 })?;
6553
6554 let worktree = worktree?;
6555 project
6556 .update(&mut cx, |project, cx| project.add_worktree(&worktree, cx))?;
6557 Ok(worktree)
6558 }
6559 .map_err(Arc::new)
6560 })
6561 .shared()
6562 })
6563 .clone();
6564 cx.background_executor().spawn(async move {
6565 match task.await {
6566 Ok(worktree) => Ok(worktree),
6567 Err(err) => Err(anyhow!("{}", err)),
6568 }
6569 })
6570 }
6571
6572 pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
6573 let mut servers_to_remove = HashMap::default();
6574 let mut servers_to_preserve = HashSet::default();
6575 for ((worktree_id, server_name), &server_id) in &self.language_server_ids {
6576 if worktree_id == &id_to_remove {
6577 servers_to_remove.insert(server_id, server_name.clone());
6578 } else {
6579 servers_to_preserve.insert(server_id);
6580 }
6581 }
6582 servers_to_remove.retain(|server_id, _| !servers_to_preserve.contains(server_id));
6583 for (server_id_to_remove, server_name) in servers_to_remove {
6584 self.language_server_ids
6585 .remove(&(id_to_remove, server_name));
6586 self.language_server_statuses.remove(&server_id_to_remove);
6587 self.last_workspace_edits_by_language_server
6588 .remove(&server_id_to_remove);
6589 self.language_servers.remove(&server_id_to_remove);
6590 cx.emit(Event::LanguageServerRemoved(server_id_to_remove));
6591 }
6592
6593 let mut prettier_instances_to_clean = FuturesUnordered::new();
6594 if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
6595 for path in prettier_paths.iter().flatten() {
6596 if let Some(prettier_instance) = self.prettier_instances.remove(path) {
6597 prettier_instances_to_clean.push(async move {
6598 prettier_instance
6599 .server()
6600 .await
6601 .map(|server| server.server_id())
6602 });
6603 }
6604 }
6605 }
6606 cx.spawn(|project, mut cx| async move {
6607 while let Some(prettier_server_id) = prettier_instances_to_clean.next().await {
6608 if let Some(prettier_server_id) = prettier_server_id {
6609 project
6610 .update(&mut cx, |project, cx| {
6611 project
6612 .supplementary_language_servers
6613 .remove(&prettier_server_id);
6614 cx.emit(Event::LanguageServerRemoved(prettier_server_id));
6615 })
6616 .ok();
6617 }
6618 }
6619 })
6620 .detach();
6621
6622 self.task_inventory().update(cx, |inventory, _| {
6623 inventory.remove_worktree_sources(id_to_remove);
6624 });
6625
6626 self.worktrees.retain(|worktree| {
6627 if let Some(worktree) = worktree.upgrade() {
6628 let id = worktree.read(cx).id();
6629 if id == id_to_remove {
6630 cx.emit(Event::WorktreeRemoved(id));
6631 false
6632 } else {
6633 true
6634 }
6635 } else {
6636 false
6637 }
6638 });
6639 self.metadata_changed(cx);
6640 }
6641
6642 fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
6643 cx.observe(worktree, |_, _, cx| cx.notify()).detach();
6644 if worktree.read(cx).is_local() {
6645 cx.subscribe(worktree, |this, worktree, event, cx| match event {
6646 worktree::Event::UpdatedEntries(changes) => {
6647 this.update_local_worktree_buffers(&worktree, changes, cx);
6648 this.update_local_worktree_language_servers(&worktree, changes, cx);
6649 this.update_local_worktree_settings(&worktree, changes, cx);
6650 this.update_prettier_settings(&worktree, changes, cx);
6651 cx.emit(Event::WorktreeUpdatedEntries(
6652 worktree.read(cx).id(),
6653 changes.clone(),
6654 ));
6655 }
6656 worktree::Event::UpdatedGitRepositories(updated_repos) => {
6657 this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx)
6658 }
6659 })
6660 .detach();
6661 }
6662
6663 let push_strong_handle = {
6664 let worktree = worktree.read(cx);
6665 self.is_shared() || worktree.is_visible() || worktree.is_remote()
6666 };
6667 if push_strong_handle {
6668 self.worktrees
6669 .push(WorktreeHandle::Strong(worktree.clone()));
6670 } else {
6671 self.worktrees
6672 .push(WorktreeHandle::Weak(worktree.downgrade()));
6673 }
6674
6675 let handle_id = worktree.entity_id();
6676 cx.observe_release(worktree, move |this, worktree, cx| {
6677 let _ = this.remove_worktree(worktree.id(), cx);
6678 cx.update_global::<SettingsStore, _>(|store, cx| {
6679 store
6680 .clear_local_settings(handle_id.as_u64() as usize, cx)
6681 .log_err()
6682 });
6683 })
6684 .detach();
6685
6686 cx.emit(Event::WorktreeAdded);
6687 self.metadata_changed(cx);
6688 }
6689
6690 fn update_local_worktree_buffers(
6691 &mut self,
6692 worktree_handle: &Model<Worktree>,
6693 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
6694 cx: &mut ModelContext<Self>,
6695 ) {
6696 let snapshot = worktree_handle.read(cx).snapshot();
6697
6698 let mut renamed_buffers = Vec::new();
6699 for (path, entry_id, _) in changes {
6700 let worktree_id = worktree_handle.read(cx).id();
6701 let project_path = ProjectPath {
6702 worktree_id,
6703 path: path.clone(),
6704 };
6705
6706 let buffer_id = match self.local_buffer_ids_by_entry_id.get(entry_id) {
6707 Some(&buffer_id) => buffer_id,
6708 None => match self.local_buffer_ids_by_path.get(&project_path) {
6709 Some(&buffer_id) => buffer_id,
6710 None => {
6711 continue;
6712 }
6713 },
6714 };
6715
6716 let open_buffer = self.opened_buffers.get(&buffer_id);
6717 let buffer = if let Some(buffer) = open_buffer.and_then(|buffer| buffer.upgrade()) {
6718 buffer
6719 } else {
6720 self.opened_buffers.remove(&buffer_id);
6721 self.local_buffer_ids_by_path.remove(&project_path);
6722 self.local_buffer_ids_by_entry_id.remove(entry_id);
6723 continue;
6724 };
6725
6726 buffer.update(cx, |buffer, cx| {
6727 if let Some(old_file) = File::from_dyn(buffer.file()) {
6728 if old_file.worktree != *worktree_handle {
6729 return;
6730 }
6731
6732 let new_file = if let Some(entry) = old_file
6733 .entry_id
6734 .and_then(|entry_id| snapshot.entry_for_id(entry_id))
6735 {
6736 File {
6737 is_local: true,
6738 entry_id: Some(entry.id),
6739 mtime: entry.mtime,
6740 path: entry.path.clone(),
6741 worktree: worktree_handle.clone(),
6742 is_deleted: false,
6743 is_private: entry.is_private,
6744 }
6745 } else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) {
6746 File {
6747 is_local: true,
6748 entry_id: Some(entry.id),
6749 mtime: entry.mtime,
6750 path: entry.path.clone(),
6751 worktree: worktree_handle.clone(),
6752 is_deleted: false,
6753 is_private: entry.is_private,
6754 }
6755 } else {
6756 File {
6757 is_local: true,
6758 entry_id: old_file.entry_id,
6759 path: old_file.path().clone(),
6760 mtime: old_file.mtime(),
6761 worktree: worktree_handle.clone(),
6762 is_deleted: true,
6763 is_private: old_file.is_private,
6764 }
6765 };
6766
6767 let old_path = old_file.abs_path(cx);
6768 if new_file.abs_path(cx) != old_path {
6769 renamed_buffers.push((cx.handle(), old_file.clone()));
6770 self.local_buffer_ids_by_path.remove(&project_path);
6771 self.local_buffer_ids_by_path.insert(
6772 ProjectPath {
6773 worktree_id,
6774 path: path.clone(),
6775 },
6776 buffer_id,
6777 );
6778 }
6779
6780 if new_file.entry_id != Some(*entry_id) {
6781 self.local_buffer_ids_by_entry_id.remove(entry_id);
6782 if let Some(entry_id) = new_file.entry_id {
6783 self.local_buffer_ids_by_entry_id
6784 .insert(entry_id, buffer_id);
6785 }
6786 }
6787
6788 if new_file != *old_file {
6789 if let Some(project_id) = self.remote_id() {
6790 self.client
6791 .send(proto::UpdateBufferFile {
6792 project_id,
6793 buffer_id: buffer_id.into(),
6794 file: Some(new_file.to_proto()),
6795 })
6796 .log_err();
6797 }
6798
6799 buffer.file_updated(Arc::new(new_file), cx);
6800 }
6801 }
6802 });
6803 }
6804
6805 for (buffer, old_file) in renamed_buffers {
6806 self.unregister_buffer_from_language_servers(&buffer, &old_file, cx);
6807 self.detect_language_for_buffer(&buffer, cx);
6808 self.register_buffer_with_language_servers(&buffer, cx);
6809 }
6810 }
6811
6812 fn update_local_worktree_language_servers(
6813 &mut self,
6814 worktree_handle: &Model<Worktree>,
6815 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
6816 cx: &mut ModelContext<Self>,
6817 ) {
6818 if changes.is_empty() {
6819 return;
6820 }
6821
6822 let worktree_id = worktree_handle.read(cx).id();
6823 let mut language_server_ids = self
6824 .language_server_ids
6825 .iter()
6826 .filter_map(|((server_worktree_id, _), server_id)| {
6827 (*server_worktree_id == worktree_id).then_some(*server_id)
6828 })
6829 .collect::<Vec<_>>();
6830 language_server_ids.sort();
6831 language_server_ids.dedup();
6832
6833 let abs_path = worktree_handle.read(cx).abs_path();
6834 for server_id in &language_server_ids {
6835 if let Some(LanguageServerState::Running {
6836 server,
6837 watched_paths,
6838 ..
6839 }) = self.language_servers.get(server_id)
6840 {
6841 if let Some(watched_paths) = watched_paths.get(&worktree_id) {
6842 let params = lsp::DidChangeWatchedFilesParams {
6843 changes: changes
6844 .iter()
6845 .filter_map(|(path, _, change)| {
6846 if !watched_paths.is_match(&path) {
6847 return None;
6848 }
6849 let typ = match change {
6850 PathChange::Loaded => return None,
6851 PathChange::Added => lsp::FileChangeType::CREATED,
6852 PathChange::Removed => lsp::FileChangeType::DELETED,
6853 PathChange::Updated => lsp::FileChangeType::CHANGED,
6854 PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
6855 };
6856 Some(lsp::FileEvent {
6857 uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
6858 typ,
6859 })
6860 })
6861 .collect(),
6862 };
6863
6864 if !params.changes.is_empty() {
6865 server
6866 .notify::<lsp::notification::DidChangeWatchedFiles>(params)
6867 .log_err();
6868 }
6869 }
6870 }
6871 }
6872 }
6873
6874 fn update_local_worktree_buffers_git_repos(
6875 &mut self,
6876 worktree_handle: Model<Worktree>,
6877 changed_repos: &UpdatedGitRepositoriesSet,
6878 cx: &mut ModelContext<Self>,
6879 ) {
6880 debug_assert!(worktree_handle.read(cx).is_local());
6881
6882 // Identify the loading buffers whose containing repository that has changed.
6883 let future_buffers = self
6884 .loading_buffers_by_path
6885 .iter()
6886 .filter_map(|(project_path, receiver)| {
6887 if project_path.worktree_id != worktree_handle.read(cx).id() {
6888 return None;
6889 }
6890 let path = &project_path.path;
6891 changed_repos
6892 .iter()
6893 .find(|(work_dir, _)| path.starts_with(work_dir))?;
6894 let receiver = receiver.clone();
6895 let path = path.clone();
6896 Some(async move {
6897 wait_for_loading_buffer(receiver)
6898 .await
6899 .ok()
6900 .map(|buffer| (buffer, path))
6901 })
6902 })
6903 .collect::<FuturesUnordered<_>>();
6904
6905 // Identify the current buffers whose containing repository has changed.
6906 let current_buffers = self
6907 .opened_buffers
6908 .values()
6909 .filter_map(|buffer| {
6910 let buffer = buffer.upgrade()?;
6911 let file = File::from_dyn(buffer.read(cx).file())?;
6912 if file.worktree != worktree_handle {
6913 return None;
6914 }
6915 let path = file.path();
6916 changed_repos
6917 .iter()
6918 .find(|(work_dir, _)| path.starts_with(work_dir))?;
6919 Some((buffer, path.clone()))
6920 })
6921 .collect::<Vec<_>>();
6922
6923 if future_buffers.len() + current_buffers.len() == 0 {
6924 return;
6925 }
6926
6927 let remote_id = self.remote_id();
6928 let client = self.client.clone();
6929 cx.spawn(move |_, mut cx| async move {
6930 // Wait for all of the buffers to load.
6931 let future_buffers = future_buffers.collect::<Vec<_>>().await;
6932
6933 // Reload the diff base for every buffer whose containing git repository has changed.
6934 let snapshot =
6935 worktree_handle.update(&mut cx, |tree, _| tree.as_local().unwrap().snapshot())?;
6936 let diff_bases_by_buffer = cx
6937 .background_executor()
6938 .spawn(async move {
6939 future_buffers
6940 .into_iter()
6941 .filter_map(|e| e)
6942 .chain(current_buffers)
6943 .filter_map(|(buffer, path)| {
6944 let (work_directory, repo) =
6945 snapshot.repository_and_work_directory_for_path(&path)?;
6946 let repo = snapshot.get_local_repo(&repo)?;
6947 let relative_path = path.strip_prefix(&work_directory).ok()?;
6948 let base_text = repo.load_index_text(relative_path);
6949 Some((buffer, base_text))
6950 })
6951 .collect::<Vec<_>>()
6952 })
6953 .await;
6954
6955 // Assign the new diff bases on all of the buffers.
6956 for (buffer, diff_base) in diff_bases_by_buffer {
6957 let buffer_id = buffer.update(&mut cx, |buffer, cx| {
6958 buffer.set_diff_base(diff_base.clone(), cx);
6959 buffer.remote_id().into()
6960 })?;
6961 if let Some(project_id) = remote_id {
6962 client
6963 .send(proto::UpdateDiffBase {
6964 project_id,
6965 buffer_id,
6966 diff_base,
6967 })
6968 .log_err();
6969 }
6970 }
6971
6972 anyhow::Ok(())
6973 })
6974 .detach();
6975 }
6976
6977 fn update_local_worktree_settings(
6978 &mut self,
6979 worktree: &Model<Worktree>,
6980 changes: &UpdatedEntriesSet,
6981 cx: &mut ModelContext<Self>,
6982 ) {
6983 if worktree.read(cx).as_local().is_none() {
6984 return;
6985 }
6986 let project_id = self.remote_id();
6987 let worktree_id = worktree.entity_id();
6988 let remote_worktree_id = worktree.read(cx).id();
6989
6990 let mut settings_contents = Vec::new();
6991 for (path, _, change) in changes.iter() {
6992 let removed = change == &PathChange::Removed;
6993 let abs_path = match worktree.read(cx).absolutize(path) {
6994 Ok(abs_path) => abs_path,
6995 Err(e) => {
6996 log::warn!("Cannot absolutize {path:?} received as {change:?} FS change: {e}");
6997 continue;
6998 }
6999 };
7000
7001 if abs_path.ends_with(&*LOCAL_SETTINGS_RELATIVE_PATH) {
7002 let settings_dir = Arc::from(
7003 path.ancestors()
7004 .nth(LOCAL_SETTINGS_RELATIVE_PATH.components().count())
7005 .unwrap(),
7006 );
7007 let fs = self.fs.clone();
7008 settings_contents.push(async move {
7009 (
7010 settings_dir,
7011 if removed {
7012 None
7013 } else {
7014 Some(async move { fs.load(&abs_path).await }.await)
7015 },
7016 )
7017 });
7018 } else if abs_path.ends_with(&*LOCAL_TASKS_RELATIVE_PATH) {
7019 self.task_inventory().update(cx, |task_inventory, cx| {
7020 if removed {
7021 task_inventory.remove_local_static_source(&abs_path);
7022 } else {
7023 let fs = self.fs.clone();
7024 let task_abs_path = abs_path.clone();
7025 task_inventory.add_source(
7026 TaskSourceKind::Worktree {
7027 id: remote_worktree_id,
7028 abs_path,
7029 },
7030 |cx| {
7031 let tasks_file_rx =
7032 watch_config_file(&cx.background_executor(), fs, task_abs_path);
7033 StaticSource::new(
7034 format!("local_tasks_for_workspace_{remote_worktree_id}"),
7035 tasks_file_rx,
7036 cx,
7037 )
7038 },
7039 cx,
7040 );
7041 }
7042 })
7043 }
7044 }
7045
7046 if settings_contents.is_empty() {
7047 return;
7048 }
7049
7050 let client = self.client.clone();
7051 cx.spawn(move |_, cx| async move {
7052 let settings_contents: Vec<(Arc<Path>, _)> =
7053 futures::future::join_all(settings_contents).await;
7054 cx.update(|cx| {
7055 cx.update_global::<SettingsStore, _>(|store, cx| {
7056 for (directory, file_content) in settings_contents {
7057 let file_content = file_content.and_then(|content| content.log_err());
7058 store
7059 .set_local_settings(
7060 worktree_id.as_u64() as usize,
7061 directory.clone(),
7062 file_content.as_ref().map(String::as_str),
7063 cx,
7064 )
7065 .log_err();
7066 if let Some(remote_id) = project_id {
7067 client
7068 .send(proto::UpdateWorktreeSettings {
7069 project_id: remote_id,
7070 worktree_id: remote_worktree_id.to_proto(),
7071 path: directory.to_string_lossy().into_owned(),
7072 content: file_content,
7073 })
7074 .log_err();
7075 }
7076 }
7077 });
7078 })
7079 .ok();
7080 })
7081 .detach();
7082 }
7083
7084 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
7085 let new_active_entry = entry.and_then(|project_path| {
7086 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
7087 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
7088 Some(entry.id)
7089 });
7090 if new_active_entry != self.active_entry {
7091 self.active_entry = new_active_entry;
7092 cx.emit(Event::ActiveEntryChanged(new_active_entry));
7093 }
7094 }
7095
7096 pub fn language_servers_running_disk_based_diagnostics(
7097 &self,
7098 ) -> impl Iterator<Item = LanguageServerId> + '_ {
7099 self.language_server_statuses
7100 .iter()
7101 .filter_map(|(id, status)| {
7102 if status.has_pending_diagnostic_updates {
7103 Some(*id)
7104 } else {
7105 None
7106 }
7107 })
7108 }
7109
7110 pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
7111 let mut summary = DiagnosticSummary::default();
7112 for (_, _, path_summary) in
7113 self.diagnostic_summaries(include_ignored, cx)
7114 .filter(|(path, _, _)| {
7115 let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
7116 include_ignored || worktree == Some(false)
7117 })
7118 {
7119 summary.error_count += path_summary.error_count;
7120 summary.warning_count += path_summary.warning_count;
7121 }
7122 summary
7123 }
7124
7125 pub fn diagnostic_summaries<'a>(
7126 &'a self,
7127 include_ignored: bool,
7128 cx: &'a AppContext,
7129 ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
7130 self.visible_worktrees(cx)
7131 .flat_map(move |worktree| {
7132 let worktree = worktree.read(cx);
7133 let worktree_id = worktree.id();
7134 worktree
7135 .diagnostic_summaries()
7136 .map(move |(path, server_id, summary)| {
7137 (ProjectPath { worktree_id, path }, server_id, summary)
7138 })
7139 })
7140 .filter(move |(path, _, _)| {
7141 let worktree = self.entry_for_path(path, cx).map(|entry| entry.is_ignored);
7142 include_ignored || worktree == Some(false)
7143 })
7144 }
7145
7146 pub fn disk_based_diagnostics_started(
7147 &mut self,
7148 language_server_id: LanguageServerId,
7149 cx: &mut ModelContext<Self>,
7150 ) {
7151 cx.emit(Event::DiskBasedDiagnosticsStarted { language_server_id });
7152 }
7153
7154 pub fn disk_based_diagnostics_finished(
7155 &mut self,
7156 language_server_id: LanguageServerId,
7157 cx: &mut ModelContext<Self>,
7158 ) {
7159 cx.emit(Event::DiskBasedDiagnosticsFinished { language_server_id });
7160 }
7161
7162 pub fn active_entry(&self) -> Option<ProjectEntryId> {
7163 self.active_entry
7164 }
7165
7166 pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
7167 self.worktree_for_id(path.worktree_id, cx)?
7168 .read(cx)
7169 .entry_for_path(&path.path)
7170 .cloned()
7171 }
7172
7173 pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
7174 let worktree = self.worktree_for_entry(entry_id, cx)?;
7175 let worktree = worktree.read(cx);
7176 let worktree_id = worktree.id();
7177 let path = worktree.entry_for_id(entry_id)?.path.clone();
7178 Some(ProjectPath { worktree_id, path })
7179 }
7180
7181 pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
7182 let workspace_root = self
7183 .worktree_for_id(project_path.worktree_id, cx)?
7184 .read(cx)
7185 .abs_path();
7186 let project_path = project_path.path.as_ref();
7187
7188 Some(if project_path == Path::new("") {
7189 workspace_root.to_path_buf()
7190 } else {
7191 workspace_root.join(project_path)
7192 })
7193 }
7194
7195 // RPC message handlers
7196
7197 async fn handle_unshare_project(
7198 this: Model<Self>,
7199 _: TypedEnvelope<proto::UnshareProject>,
7200 _: Arc<Client>,
7201 mut cx: AsyncAppContext,
7202 ) -> Result<()> {
7203 this.update(&mut cx, |this, cx| {
7204 if this.is_local() {
7205 this.unshare(cx)?;
7206 } else {
7207 this.disconnected_from_host(cx);
7208 }
7209 Ok(())
7210 })?
7211 }
7212
7213 async fn handle_add_collaborator(
7214 this: Model<Self>,
7215 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
7216 _: Arc<Client>,
7217 mut cx: AsyncAppContext,
7218 ) -> Result<()> {
7219 let collaborator = envelope
7220 .payload
7221 .collaborator
7222 .take()
7223 .ok_or_else(|| anyhow!("empty collaborator"))?;
7224
7225 let collaborator = Collaborator::from_proto(collaborator)?;
7226 this.update(&mut cx, |this, cx| {
7227 this.shared_buffers.remove(&collaborator.peer_id);
7228 cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
7229 this.collaborators
7230 .insert(collaborator.peer_id, collaborator);
7231 cx.notify();
7232 })?;
7233
7234 Ok(())
7235 }
7236
7237 async fn handle_update_project_collaborator(
7238 this: Model<Self>,
7239 envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
7240 _: Arc<Client>,
7241 mut cx: AsyncAppContext,
7242 ) -> Result<()> {
7243 let old_peer_id = envelope
7244 .payload
7245 .old_peer_id
7246 .ok_or_else(|| anyhow!("missing old peer id"))?;
7247 let new_peer_id = envelope
7248 .payload
7249 .new_peer_id
7250 .ok_or_else(|| anyhow!("missing new peer id"))?;
7251 this.update(&mut cx, |this, cx| {
7252 let collaborator = this
7253 .collaborators
7254 .remove(&old_peer_id)
7255 .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
7256 let is_host = collaborator.replica_id == 0;
7257 this.collaborators.insert(new_peer_id, collaborator);
7258
7259 let buffers = this.shared_buffers.remove(&old_peer_id);
7260 log::info!(
7261 "peer {} became {}. moving buffers {:?}",
7262 old_peer_id,
7263 new_peer_id,
7264 &buffers
7265 );
7266 if let Some(buffers) = buffers {
7267 this.shared_buffers.insert(new_peer_id, buffers);
7268 }
7269
7270 if is_host {
7271 this.opened_buffers
7272 .retain(|_, buffer| !matches!(buffer, OpenBuffer::Operations(_)));
7273 this.buffer_ordered_messages_tx
7274 .unbounded_send(BufferOrderedMessage::Resync)
7275 .unwrap();
7276 }
7277
7278 cx.emit(Event::CollaboratorUpdated {
7279 old_peer_id,
7280 new_peer_id,
7281 });
7282 cx.notify();
7283 Ok(())
7284 })?
7285 }
7286
7287 async fn handle_remove_collaborator(
7288 this: Model<Self>,
7289 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
7290 _: Arc<Client>,
7291 mut cx: AsyncAppContext,
7292 ) -> Result<()> {
7293 this.update(&mut cx, |this, cx| {
7294 let peer_id = envelope
7295 .payload
7296 .peer_id
7297 .ok_or_else(|| anyhow!("invalid peer id"))?;
7298 let replica_id = this
7299 .collaborators
7300 .remove(&peer_id)
7301 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
7302 .replica_id;
7303 for buffer in this.opened_buffers.values() {
7304 if let Some(buffer) = buffer.upgrade() {
7305 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
7306 }
7307 }
7308 this.shared_buffers.remove(&peer_id);
7309
7310 cx.emit(Event::CollaboratorLeft(peer_id));
7311 cx.notify();
7312 Ok(())
7313 })?
7314 }
7315
7316 async fn handle_update_project(
7317 this: Model<Self>,
7318 envelope: TypedEnvelope<proto::UpdateProject>,
7319 _: Arc<Client>,
7320 mut cx: AsyncAppContext,
7321 ) -> Result<()> {
7322 this.update(&mut cx, |this, cx| {
7323 // Don't handle messages that were sent before the response to us joining the project
7324 if envelope.message_id > this.join_project_response_message_id {
7325 this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
7326 }
7327 Ok(())
7328 })?
7329 }
7330
7331 async fn handle_update_worktree(
7332 this: Model<Self>,
7333 envelope: TypedEnvelope<proto::UpdateWorktree>,
7334 _: Arc<Client>,
7335 mut cx: AsyncAppContext,
7336 ) -> Result<()> {
7337 this.update(&mut cx, |this, cx| {
7338 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7339 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7340 worktree.update(cx, |worktree, _| {
7341 let worktree = worktree.as_remote_mut().unwrap();
7342 worktree.update_from_remote(envelope.payload);
7343 });
7344 }
7345 Ok(())
7346 })?
7347 }
7348
7349 async fn handle_update_worktree_settings(
7350 this: Model<Self>,
7351 envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
7352 _: Arc<Client>,
7353 mut cx: AsyncAppContext,
7354 ) -> Result<()> {
7355 this.update(&mut cx, |this, cx| {
7356 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7357 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7358 cx.update_global::<SettingsStore, _>(|store, cx| {
7359 store
7360 .set_local_settings(
7361 worktree.entity_id().as_u64() as usize,
7362 PathBuf::from(&envelope.payload.path).into(),
7363 envelope.payload.content.as_ref().map(String::as_str),
7364 cx,
7365 )
7366 .log_err();
7367 });
7368 }
7369 Ok(())
7370 })?
7371 }
7372
7373 async fn handle_create_project_entry(
7374 this: Model<Self>,
7375 envelope: TypedEnvelope<proto::CreateProjectEntry>,
7376 _: Arc<Client>,
7377 mut cx: AsyncAppContext,
7378 ) -> Result<proto::ProjectEntryResponse> {
7379 let worktree = this.update(&mut cx, |this, cx| {
7380 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7381 this.worktree_for_id(worktree_id, cx)
7382 .ok_or_else(|| anyhow!("worktree not found"))
7383 })??;
7384 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7385 let entry = worktree
7386 .update(&mut cx, |worktree, cx| {
7387 let worktree = worktree.as_local_mut().unwrap();
7388 let path = PathBuf::from(envelope.payload.path);
7389 worktree.create_entry(path, envelope.payload.is_directory, cx)
7390 })?
7391 .await?;
7392 Ok(proto::ProjectEntryResponse {
7393 entry: entry.as_ref().map(|e| e.into()),
7394 worktree_scan_id: worktree_scan_id as u64,
7395 })
7396 }
7397
7398 async fn handle_rename_project_entry(
7399 this: Model<Self>,
7400 envelope: TypedEnvelope<proto::RenameProjectEntry>,
7401 _: Arc<Client>,
7402 mut cx: AsyncAppContext,
7403 ) -> Result<proto::ProjectEntryResponse> {
7404 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7405 let worktree = this.update(&mut cx, |this, cx| {
7406 this.worktree_for_entry(entry_id, cx)
7407 .ok_or_else(|| anyhow!("worktree not found"))
7408 })??;
7409 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7410 let entry = worktree
7411 .update(&mut cx, |worktree, cx| {
7412 let new_path = PathBuf::from(envelope.payload.new_path);
7413 worktree
7414 .as_local_mut()
7415 .unwrap()
7416 .rename_entry(entry_id, new_path, cx)
7417 })?
7418 .await?;
7419 Ok(proto::ProjectEntryResponse {
7420 entry: entry.as_ref().map(|e| e.into()),
7421 worktree_scan_id: worktree_scan_id as u64,
7422 })
7423 }
7424
7425 async fn handle_copy_project_entry(
7426 this: Model<Self>,
7427 envelope: TypedEnvelope<proto::CopyProjectEntry>,
7428 _: Arc<Client>,
7429 mut cx: AsyncAppContext,
7430 ) -> Result<proto::ProjectEntryResponse> {
7431 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7432 let worktree = this.update(&mut cx, |this, cx| {
7433 this.worktree_for_entry(entry_id, cx)
7434 .ok_or_else(|| anyhow!("worktree not found"))
7435 })??;
7436 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7437 let entry = worktree
7438 .update(&mut cx, |worktree, cx| {
7439 let new_path = PathBuf::from(envelope.payload.new_path);
7440 worktree
7441 .as_local_mut()
7442 .unwrap()
7443 .copy_entry(entry_id, new_path, cx)
7444 })?
7445 .await?;
7446 Ok(proto::ProjectEntryResponse {
7447 entry: entry.as_ref().map(|e| e.into()),
7448 worktree_scan_id: worktree_scan_id as u64,
7449 })
7450 }
7451
7452 async fn handle_delete_project_entry(
7453 this: Model<Self>,
7454 envelope: TypedEnvelope<proto::DeleteProjectEntry>,
7455 _: Arc<Client>,
7456 mut cx: AsyncAppContext,
7457 ) -> Result<proto::ProjectEntryResponse> {
7458 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7459
7460 this.update(&mut cx, |_, cx| cx.emit(Event::DeletedEntry(entry_id)))?;
7461
7462 let worktree = this.update(&mut cx, |this, cx| {
7463 this.worktree_for_entry(entry_id, cx)
7464 .ok_or_else(|| anyhow!("worktree not found"))
7465 })??;
7466 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())?;
7467 worktree
7468 .update(&mut cx, |worktree, cx| {
7469 worktree
7470 .as_local_mut()
7471 .unwrap()
7472 .delete_entry(entry_id, cx)
7473 .ok_or_else(|| anyhow!("invalid entry"))
7474 })??
7475 .await?;
7476 Ok(proto::ProjectEntryResponse {
7477 entry: None,
7478 worktree_scan_id: worktree_scan_id as u64,
7479 })
7480 }
7481
7482 async fn handle_expand_project_entry(
7483 this: Model<Self>,
7484 envelope: TypedEnvelope<proto::ExpandProjectEntry>,
7485 _: Arc<Client>,
7486 mut cx: AsyncAppContext,
7487 ) -> Result<proto::ExpandProjectEntryResponse> {
7488 let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
7489 let worktree = this
7490 .update(&mut cx, |this, cx| this.worktree_for_entry(entry_id, cx))?
7491 .ok_or_else(|| anyhow!("invalid request"))?;
7492 worktree
7493 .update(&mut cx, |worktree, cx| {
7494 worktree
7495 .as_local_mut()
7496 .unwrap()
7497 .expand_entry(entry_id, cx)
7498 .ok_or_else(|| anyhow!("invalid entry"))
7499 })??
7500 .await?;
7501 let worktree_scan_id = worktree.update(&mut cx, |worktree, _| worktree.scan_id())? as u64;
7502 Ok(proto::ExpandProjectEntryResponse { worktree_scan_id })
7503 }
7504
7505 async fn handle_update_diagnostic_summary(
7506 this: Model<Self>,
7507 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
7508 _: Arc<Client>,
7509 mut cx: AsyncAppContext,
7510 ) -> Result<()> {
7511 this.update(&mut cx, |this, cx| {
7512 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
7513 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
7514 if let Some(summary) = envelope.payload.summary {
7515 let project_path = ProjectPath {
7516 worktree_id,
7517 path: Path::new(&summary.path).into(),
7518 };
7519 worktree.update(cx, |worktree, _| {
7520 worktree
7521 .as_remote_mut()
7522 .unwrap()
7523 .update_diagnostic_summary(project_path.path.clone(), &summary);
7524 });
7525 cx.emit(Event::DiagnosticsUpdated {
7526 language_server_id: LanguageServerId(summary.language_server_id as usize),
7527 path: project_path,
7528 });
7529 }
7530 }
7531 Ok(())
7532 })?
7533 }
7534
7535 async fn handle_start_language_server(
7536 this: Model<Self>,
7537 envelope: TypedEnvelope<proto::StartLanguageServer>,
7538 _: Arc<Client>,
7539 mut cx: AsyncAppContext,
7540 ) -> Result<()> {
7541 let server = envelope
7542 .payload
7543 .server
7544 .ok_or_else(|| anyhow!("invalid server"))?;
7545 this.update(&mut cx, |this, cx| {
7546 this.language_server_statuses.insert(
7547 LanguageServerId(server.id as usize),
7548 LanguageServerStatus {
7549 name: server.name,
7550 pending_work: Default::default(),
7551 has_pending_diagnostic_updates: false,
7552 progress_tokens: Default::default(),
7553 },
7554 );
7555 cx.notify();
7556 })?;
7557 Ok(())
7558 }
7559
7560 async fn handle_update_language_server(
7561 this: Model<Self>,
7562 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
7563 _: Arc<Client>,
7564 mut cx: AsyncAppContext,
7565 ) -> Result<()> {
7566 this.update(&mut cx, |this, cx| {
7567 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
7568
7569 match envelope
7570 .payload
7571 .variant
7572 .ok_or_else(|| anyhow!("invalid variant"))?
7573 {
7574 proto::update_language_server::Variant::WorkStart(payload) => {
7575 this.on_lsp_work_start(
7576 language_server_id,
7577 payload.token,
7578 LanguageServerProgress {
7579 message: payload.message,
7580 percentage: payload.percentage.map(|p| p as usize),
7581 last_update_at: Instant::now(),
7582 },
7583 cx,
7584 );
7585 }
7586
7587 proto::update_language_server::Variant::WorkProgress(payload) => {
7588 this.on_lsp_work_progress(
7589 language_server_id,
7590 payload.token,
7591 LanguageServerProgress {
7592 message: payload.message,
7593 percentage: payload.percentage.map(|p| p as usize),
7594 last_update_at: Instant::now(),
7595 },
7596 cx,
7597 );
7598 }
7599
7600 proto::update_language_server::Variant::WorkEnd(payload) => {
7601 this.on_lsp_work_end(language_server_id, payload.token, cx);
7602 }
7603
7604 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
7605 this.disk_based_diagnostics_started(language_server_id, cx);
7606 }
7607
7608 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
7609 this.disk_based_diagnostics_finished(language_server_id, cx)
7610 }
7611 }
7612
7613 Ok(())
7614 })?
7615 }
7616
7617 async fn handle_update_buffer(
7618 this: Model<Self>,
7619 envelope: TypedEnvelope<proto::UpdateBuffer>,
7620 _: Arc<Client>,
7621 mut cx: AsyncAppContext,
7622 ) -> Result<proto::Ack> {
7623 this.update(&mut cx, |this, cx| {
7624 let payload = envelope.payload.clone();
7625 let buffer_id = BufferId::new(payload.buffer_id)?;
7626 let ops = payload
7627 .operations
7628 .into_iter()
7629 .map(language::proto::deserialize_operation)
7630 .collect::<Result<Vec<_>, _>>()?;
7631 let is_remote = this.is_remote();
7632 match this.opened_buffers.entry(buffer_id) {
7633 hash_map::Entry::Occupied(mut e) => match e.get_mut() {
7634 OpenBuffer::Strong(buffer) => {
7635 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
7636 }
7637 OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops),
7638 OpenBuffer::Weak(_) => {}
7639 },
7640 hash_map::Entry::Vacant(e) => {
7641 assert!(
7642 is_remote,
7643 "received buffer update from {:?}",
7644 envelope.original_sender_id
7645 );
7646 e.insert(OpenBuffer::Operations(ops));
7647 }
7648 }
7649 Ok(proto::Ack {})
7650 })?
7651 }
7652
7653 async fn handle_create_buffer_for_peer(
7654 this: Model<Self>,
7655 envelope: TypedEnvelope<proto::CreateBufferForPeer>,
7656 _: Arc<Client>,
7657 mut cx: AsyncAppContext,
7658 ) -> Result<()> {
7659 this.update(&mut cx, |this, cx| {
7660 match envelope
7661 .payload
7662 .variant
7663 .ok_or_else(|| anyhow!("missing variant"))?
7664 {
7665 proto::create_buffer_for_peer::Variant::State(mut state) => {
7666 let mut buffer_file = None;
7667 if let Some(file) = state.file.take() {
7668 let worktree_id = WorktreeId::from_proto(file.worktree_id);
7669 let worktree = this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
7670 anyhow!("no worktree found for id {}", file.worktree_id)
7671 })?;
7672 buffer_file = Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
7673 as Arc<dyn language::File>);
7674 }
7675
7676 let buffer_id = BufferId::new(state.id)?;
7677 let buffer = cx.new_model(|_| {
7678 Buffer::from_proto(this.replica_id(), this.capability(), state, buffer_file)
7679 .unwrap()
7680 });
7681 this.incomplete_remote_buffers
7682 .insert(buffer_id, Some(buffer));
7683 }
7684 proto::create_buffer_for_peer::Variant::Chunk(chunk) => {
7685 let buffer_id = BufferId::new(chunk.buffer_id)?;
7686 let buffer = this
7687 .incomplete_remote_buffers
7688 .get(&buffer_id)
7689 .cloned()
7690 .flatten()
7691 .ok_or_else(|| {
7692 anyhow!(
7693 "received chunk for buffer {} without initial state",
7694 chunk.buffer_id
7695 )
7696 })?;
7697 let operations = chunk
7698 .operations
7699 .into_iter()
7700 .map(language::proto::deserialize_operation)
7701 .collect::<Result<Vec<_>>>()?;
7702 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
7703
7704 if chunk.is_last {
7705 this.incomplete_remote_buffers.remove(&buffer_id);
7706 this.register_buffer(&buffer, cx)?;
7707 }
7708 }
7709 }
7710
7711 Ok(())
7712 })?
7713 }
7714
7715 async fn handle_update_diff_base(
7716 this: Model<Self>,
7717 envelope: TypedEnvelope<proto::UpdateDiffBase>,
7718 _: Arc<Client>,
7719 mut cx: AsyncAppContext,
7720 ) -> Result<()> {
7721 this.update(&mut cx, |this, cx| {
7722 let buffer_id = envelope.payload.buffer_id;
7723 let buffer_id = BufferId::new(buffer_id)?;
7724 let diff_base = envelope.payload.diff_base;
7725 if let Some(buffer) = this
7726 .opened_buffers
7727 .get_mut(&buffer_id)
7728 .and_then(|b| b.upgrade())
7729 .or_else(|| {
7730 this.incomplete_remote_buffers
7731 .get(&buffer_id)
7732 .cloned()
7733 .flatten()
7734 })
7735 {
7736 buffer.update(cx, |buffer, cx| buffer.set_diff_base(diff_base, cx));
7737 }
7738 Ok(())
7739 })?
7740 }
7741
7742 async fn handle_update_buffer_file(
7743 this: Model<Self>,
7744 envelope: TypedEnvelope<proto::UpdateBufferFile>,
7745 _: Arc<Client>,
7746 mut cx: AsyncAppContext,
7747 ) -> Result<()> {
7748 let buffer_id = envelope.payload.buffer_id;
7749 let buffer_id = BufferId::new(buffer_id)?;
7750
7751 this.update(&mut cx, |this, cx| {
7752 let payload = envelope.payload.clone();
7753 if let Some(buffer) = this
7754 .opened_buffers
7755 .get(&buffer_id)
7756 .and_then(|b| b.upgrade())
7757 .or_else(|| {
7758 this.incomplete_remote_buffers
7759 .get(&buffer_id)
7760 .cloned()
7761 .flatten()
7762 })
7763 {
7764 let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
7765 let worktree = this
7766 .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
7767 .ok_or_else(|| anyhow!("no such worktree"))?;
7768 let file = File::from_proto(file, worktree, cx)?;
7769 buffer.update(cx, |buffer, cx| {
7770 buffer.file_updated(Arc::new(file), cx);
7771 });
7772 this.detect_language_for_buffer(&buffer, cx);
7773 }
7774 Ok(())
7775 })?
7776 }
7777
7778 async fn handle_save_buffer(
7779 this: Model<Self>,
7780 envelope: TypedEnvelope<proto::SaveBuffer>,
7781 _: Arc<Client>,
7782 mut cx: AsyncAppContext,
7783 ) -> Result<proto::BufferSaved> {
7784 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
7785 let (project_id, buffer) = this.update(&mut cx, |this, _cx| {
7786 let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
7787 let buffer = this
7788 .opened_buffers
7789 .get(&buffer_id)
7790 .and_then(|buffer| buffer.upgrade())
7791 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
7792 anyhow::Ok((project_id, buffer))
7793 })??;
7794 buffer
7795 .update(&mut cx, |buffer, _| {
7796 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
7797 })?
7798 .await?;
7799 let buffer_id = buffer.update(&mut cx, |buffer, _| buffer.remote_id())?;
7800
7801 this.update(&mut cx, |this, cx| this.save_buffer(buffer.clone(), cx))?
7802 .await?;
7803 Ok(buffer.update(&mut cx, |buffer, _| proto::BufferSaved {
7804 project_id,
7805 buffer_id: buffer_id.into(),
7806 version: serialize_version(buffer.saved_version()),
7807 mtime: Some(buffer.saved_mtime().into()),
7808 fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()),
7809 })?)
7810 }
7811
7812 async fn handle_reload_buffers(
7813 this: Model<Self>,
7814 envelope: TypedEnvelope<proto::ReloadBuffers>,
7815 _: Arc<Client>,
7816 mut cx: AsyncAppContext,
7817 ) -> Result<proto::ReloadBuffersResponse> {
7818 let sender_id = envelope.original_sender_id()?;
7819 let reload = this.update(&mut cx, |this, cx| {
7820 let mut buffers = HashSet::default();
7821 for buffer_id in &envelope.payload.buffer_ids {
7822 let buffer_id = BufferId::new(*buffer_id)?;
7823 buffers.insert(
7824 this.opened_buffers
7825 .get(&buffer_id)
7826 .and_then(|buffer| buffer.upgrade())
7827 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
7828 );
7829 }
7830 Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
7831 })??;
7832
7833 let project_transaction = reload.await?;
7834 let project_transaction = this.update(&mut cx, |this, cx| {
7835 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
7836 })?;
7837 Ok(proto::ReloadBuffersResponse {
7838 transaction: Some(project_transaction),
7839 })
7840 }
7841
7842 async fn handle_synchronize_buffers(
7843 this: Model<Self>,
7844 envelope: TypedEnvelope<proto::SynchronizeBuffers>,
7845 _: Arc<Client>,
7846 mut cx: AsyncAppContext,
7847 ) -> Result<proto::SynchronizeBuffersResponse> {
7848 let project_id = envelope.payload.project_id;
7849 let mut response = proto::SynchronizeBuffersResponse {
7850 buffers: Default::default(),
7851 };
7852
7853 this.update(&mut cx, |this, cx| {
7854 let Some(guest_id) = envelope.original_sender_id else {
7855 error!("missing original_sender_id on SynchronizeBuffers request");
7856 bail!("missing original_sender_id on SynchronizeBuffers request");
7857 };
7858
7859 this.shared_buffers.entry(guest_id).or_default().clear();
7860 for buffer in envelope.payload.buffers {
7861 let buffer_id = BufferId::new(buffer.id)?;
7862 let remote_version = language::proto::deserialize_version(&buffer.version);
7863 if let Some(buffer) = this.buffer_for_id(buffer_id) {
7864 this.shared_buffers
7865 .entry(guest_id)
7866 .or_default()
7867 .insert(buffer_id);
7868
7869 let buffer = buffer.read(cx);
7870 response.buffers.push(proto::BufferVersion {
7871 id: buffer_id.into(),
7872 version: language::proto::serialize_version(&buffer.version),
7873 });
7874
7875 let operations = buffer.serialize_ops(Some(remote_version), cx);
7876 let client = this.client.clone();
7877 if let Some(file) = buffer.file() {
7878 client
7879 .send(proto::UpdateBufferFile {
7880 project_id,
7881 buffer_id: buffer_id.into(),
7882 file: Some(file.to_proto()),
7883 })
7884 .log_err();
7885 }
7886
7887 client
7888 .send(proto::UpdateDiffBase {
7889 project_id,
7890 buffer_id: buffer_id.into(),
7891 diff_base: buffer.diff_base().map(Into::into),
7892 })
7893 .log_err();
7894
7895 client
7896 .send(proto::BufferReloaded {
7897 project_id,
7898 buffer_id: buffer_id.into(),
7899 version: language::proto::serialize_version(buffer.saved_version()),
7900 mtime: Some(buffer.saved_mtime().into()),
7901 fingerprint: language::proto::serialize_fingerprint(
7902 buffer.saved_version_fingerprint(),
7903 ),
7904 line_ending: language::proto::serialize_line_ending(
7905 buffer.line_ending(),
7906 ) as i32,
7907 })
7908 .log_err();
7909
7910 cx.background_executor()
7911 .spawn(
7912 async move {
7913 let operations = operations.await;
7914 for chunk in split_operations(operations) {
7915 client
7916 .request(proto::UpdateBuffer {
7917 project_id,
7918 buffer_id: buffer_id.into(),
7919 operations: chunk,
7920 })
7921 .await?;
7922 }
7923 anyhow::Ok(())
7924 }
7925 .log_err(),
7926 )
7927 .detach();
7928 }
7929 }
7930 Ok(())
7931 })??;
7932
7933 Ok(response)
7934 }
7935
7936 async fn handle_format_buffers(
7937 this: Model<Self>,
7938 envelope: TypedEnvelope<proto::FormatBuffers>,
7939 _: Arc<Client>,
7940 mut cx: AsyncAppContext,
7941 ) -> Result<proto::FormatBuffersResponse> {
7942 let sender_id = envelope.original_sender_id()?;
7943 let format = this.update(&mut cx, |this, cx| {
7944 let mut buffers = HashSet::default();
7945 for buffer_id in &envelope.payload.buffer_ids {
7946 let buffer_id = BufferId::new(*buffer_id)?;
7947 buffers.insert(
7948 this.opened_buffers
7949 .get(&buffer_id)
7950 .and_then(|buffer| buffer.upgrade())
7951 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
7952 );
7953 }
7954 let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
7955 Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
7956 })??;
7957
7958 let project_transaction = format.await?;
7959 let project_transaction = this.update(&mut cx, |this, cx| {
7960 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
7961 })?;
7962 Ok(proto::FormatBuffersResponse {
7963 transaction: Some(project_transaction),
7964 })
7965 }
7966
7967 async fn handle_apply_additional_edits_for_completion(
7968 this: Model<Self>,
7969 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
7970 _: Arc<Client>,
7971 mut cx: AsyncAppContext,
7972 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
7973 let (buffer, completion) = this.update(&mut cx, |this, cx| {
7974 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
7975 let buffer = this
7976 .opened_buffers
7977 .get(&buffer_id)
7978 .and_then(|buffer| buffer.upgrade())
7979 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
7980 let language = buffer.read(cx).language();
7981 let completion = language::proto::deserialize_completion(
7982 envelope
7983 .payload
7984 .completion
7985 .ok_or_else(|| anyhow!("invalid completion"))?,
7986 language.cloned(),
7987 );
7988 Ok::<_, anyhow::Error>((buffer, completion))
7989 })??;
7990
7991 let completion = completion.await?;
7992
7993 let apply_additional_edits = this.update(&mut cx, |this, cx| {
7994 this.apply_additional_edits_for_completion(buffer, completion, false, cx)
7995 })?;
7996
7997 Ok(proto::ApplyCompletionAdditionalEditsResponse {
7998 transaction: apply_additional_edits
7999 .await?
8000 .as_ref()
8001 .map(language::proto::serialize_transaction),
8002 })
8003 }
8004
8005 async fn handle_resolve_completion_documentation(
8006 this: Model<Self>,
8007 envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
8008 _: Arc<Client>,
8009 mut cx: AsyncAppContext,
8010 ) -> Result<proto::ResolveCompletionDocumentationResponse> {
8011 let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
8012
8013 let completion = this
8014 .read_with(&mut cx, |this, _| {
8015 let id = LanguageServerId(envelope.payload.language_server_id as usize);
8016 let Some(server) = this.language_server_for_id(id) else {
8017 return Err(anyhow!("No language server {id}"));
8018 };
8019
8020 Ok(server.request::<lsp::request::ResolveCompletionItem>(lsp_completion))
8021 })??
8022 .await?;
8023
8024 let mut is_markdown = false;
8025 let text = match completion.documentation {
8026 Some(lsp::Documentation::String(text)) => text,
8027
8028 Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
8029 is_markdown = kind == lsp::MarkupKind::Markdown;
8030 value
8031 }
8032
8033 _ => String::new(),
8034 };
8035
8036 Ok(proto::ResolveCompletionDocumentationResponse { text, is_markdown })
8037 }
8038
8039 async fn handle_apply_code_action(
8040 this: Model<Self>,
8041 envelope: TypedEnvelope<proto::ApplyCodeAction>,
8042 _: Arc<Client>,
8043 mut cx: AsyncAppContext,
8044 ) -> Result<proto::ApplyCodeActionResponse> {
8045 let sender_id = envelope.original_sender_id()?;
8046 let action = language::proto::deserialize_code_action(
8047 envelope
8048 .payload
8049 .action
8050 .ok_or_else(|| anyhow!("invalid action"))?,
8051 )?;
8052 let apply_code_action = this.update(&mut cx, |this, cx| {
8053 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8054 let buffer = this
8055 .opened_buffers
8056 .get(&buffer_id)
8057 .and_then(|buffer| buffer.upgrade())
8058 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
8059 Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
8060 })??;
8061
8062 let project_transaction = apply_code_action.await?;
8063 let project_transaction = this.update(&mut cx, |this, cx| {
8064 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
8065 })?;
8066 Ok(proto::ApplyCodeActionResponse {
8067 transaction: Some(project_transaction),
8068 })
8069 }
8070
8071 async fn handle_on_type_formatting(
8072 this: Model<Self>,
8073 envelope: TypedEnvelope<proto::OnTypeFormatting>,
8074 _: Arc<Client>,
8075 mut cx: AsyncAppContext,
8076 ) -> Result<proto::OnTypeFormattingResponse> {
8077 let on_type_formatting = this.update(&mut cx, |this, cx| {
8078 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8079 let buffer = this
8080 .opened_buffers
8081 .get(&buffer_id)
8082 .and_then(|buffer| buffer.upgrade())
8083 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
8084 let position = envelope
8085 .payload
8086 .position
8087 .and_then(deserialize_anchor)
8088 .ok_or_else(|| anyhow!("invalid position"))?;
8089 Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
8090 buffer,
8091 position,
8092 envelope.payload.trigger.clone(),
8093 cx,
8094 ))
8095 })??;
8096
8097 let transaction = on_type_formatting
8098 .await?
8099 .as_ref()
8100 .map(language::proto::serialize_transaction);
8101 Ok(proto::OnTypeFormattingResponse { transaction })
8102 }
8103
8104 async fn handle_inlay_hints(
8105 this: Model<Self>,
8106 envelope: TypedEnvelope<proto::InlayHints>,
8107 _: Arc<Client>,
8108 mut cx: AsyncAppContext,
8109 ) -> Result<proto::InlayHintsResponse> {
8110 let sender_id = envelope.original_sender_id()?;
8111 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8112 let buffer = this.update(&mut cx, |this, _| {
8113 this.opened_buffers
8114 .get(&buffer_id)
8115 .and_then(|buffer| buffer.upgrade())
8116 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
8117 })??;
8118 buffer
8119 .update(&mut cx, |buffer, _| {
8120 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
8121 })?
8122 .await
8123 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
8124
8125 let start = envelope
8126 .payload
8127 .start
8128 .and_then(deserialize_anchor)
8129 .context("missing range start")?;
8130 let end = envelope
8131 .payload
8132 .end
8133 .and_then(deserialize_anchor)
8134 .context("missing range end")?;
8135 let buffer_hints = this
8136 .update(&mut cx, |project, cx| {
8137 project.inlay_hints(buffer.clone(), start..end, cx)
8138 })?
8139 .await
8140 .context("inlay hints fetch")?;
8141
8142 Ok(this.update(&mut cx, |project, cx| {
8143 InlayHints::response_to_proto(
8144 buffer_hints,
8145 project,
8146 sender_id,
8147 &buffer.read(cx).version(),
8148 cx,
8149 )
8150 })?)
8151 }
8152
8153 async fn handle_resolve_inlay_hint(
8154 this: Model<Self>,
8155 envelope: TypedEnvelope<proto::ResolveInlayHint>,
8156 _: Arc<Client>,
8157 mut cx: AsyncAppContext,
8158 ) -> Result<proto::ResolveInlayHintResponse> {
8159 let proto_hint = envelope
8160 .payload
8161 .hint
8162 .expect("incorrect protobuf resolve inlay hint message: missing the inlay hint");
8163 let hint = InlayHints::proto_to_project_hint(proto_hint)
8164 .context("resolved proto inlay hint conversion")?;
8165 let buffer = this.update(&mut cx, |this, _cx| {
8166 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8167 this.opened_buffers
8168 .get(&buffer_id)
8169 .and_then(|buffer| buffer.upgrade())
8170 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
8171 })??;
8172 let response_hint = this
8173 .update(&mut cx, |project, cx| {
8174 project.resolve_inlay_hint(
8175 hint,
8176 buffer,
8177 LanguageServerId(envelope.payload.language_server_id as usize),
8178 cx,
8179 )
8180 })?
8181 .await
8182 .context("inlay hints fetch")?;
8183 Ok(proto::ResolveInlayHintResponse {
8184 hint: Some(InlayHints::project_to_proto_hint(response_hint)),
8185 })
8186 }
8187
8188 async fn handle_refresh_inlay_hints(
8189 this: Model<Self>,
8190 _: TypedEnvelope<proto::RefreshInlayHints>,
8191 _: Arc<Client>,
8192 mut cx: AsyncAppContext,
8193 ) -> Result<proto::Ack> {
8194 this.update(&mut cx, |_, cx| {
8195 cx.emit(Event::RefreshInlayHints);
8196 })?;
8197 Ok(proto::Ack {})
8198 }
8199
8200 async fn handle_lsp_command<T: LspCommand>(
8201 this: Model<Self>,
8202 envelope: TypedEnvelope<T::ProtoRequest>,
8203 _: Arc<Client>,
8204 mut cx: AsyncAppContext,
8205 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
8206 where
8207 <T::LspRequest as lsp::request::Request>::Params: Send,
8208 <T::LspRequest as lsp::request::Request>::Result: Send,
8209 {
8210 let sender_id = envelope.original_sender_id()?;
8211 let buffer_id = T::buffer_id_from_proto(&envelope.payload)?;
8212 let buffer_handle = this.update(&mut cx, |this, _cx| {
8213 this.opened_buffers
8214 .get(&buffer_id)
8215 .and_then(|buffer| buffer.upgrade())
8216 .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
8217 })??;
8218 let request = T::from_proto(
8219 envelope.payload,
8220 this.clone(),
8221 buffer_handle.clone(),
8222 cx.clone(),
8223 )
8224 .await?;
8225 let response = this
8226 .update(&mut cx, |this, cx| {
8227 this.request_lsp(
8228 buffer_handle.clone(),
8229 LanguageServerToQuery::Primary,
8230 request,
8231 cx,
8232 )
8233 })?
8234 .await?;
8235 this.update(&mut cx, |this, cx| {
8236 Ok(T::response_to_proto(
8237 response,
8238 this,
8239 sender_id,
8240 &buffer_handle.read(cx).version(),
8241 cx,
8242 ))
8243 })?
8244 }
8245
8246 async fn handle_get_project_symbols(
8247 this: Model<Self>,
8248 envelope: TypedEnvelope<proto::GetProjectSymbols>,
8249 _: Arc<Client>,
8250 mut cx: AsyncAppContext,
8251 ) -> Result<proto::GetProjectSymbolsResponse> {
8252 let symbols = this
8253 .update(&mut cx, |this, cx| {
8254 this.symbols(&envelope.payload.query, cx)
8255 })?
8256 .await?;
8257
8258 Ok(proto::GetProjectSymbolsResponse {
8259 symbols: symbols.iter().map(serialize_symbol).collect(),
8260 })
8261 }
8262
8263 async fn handle_search_project(
8264 this: Model<Self>,
8265 envelope: TypedEnvelope<proto::SearchProject>,
8266 _: Arc<Client>,
8267 mut cx: AsyncAppContext,
8268 ) -> Result<proto::SearchProjectResponse> {
8269 let peer_id = envelope.original_sender_id()?;
8270 let query = SearchQuery::from_proto(envelope.payload)?;
8271 let mut result = this.update(&mut cx, |this, cx| this.search(query, cx))?;
8272
8273 cx.spawn(move |mut cx| async move {
8274 let mut locations = Vec::new();
8275 while let Some((buffer, ranges)) = result.next().await {
8276 for range in ranges {
8277 let start = serialize_anchor(&range.start);
8278 let end = serialize_anchor(&range.end);
8279 let buffer_id = this.update(&mut cx, |this, cx| {
8280 this.create_buffer_for_peer(&buffer, peer_id, cx).into()
8281 })?;
8282 locations.push(proto::Location {
8283 buffer_id,
8284 start: Some(start),
8285 end: Some(end),
8286 });
8287 }
8288 }
8289 Ok(proto::SearchProjectResponse { locations })
8290 })
8291 .await
8292 }
8293
8294 async fn handle_open_buffer_for_symbol(
8295 this: Model<Self>,
8296 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
8297 _: Arc<Client>,
8298 mut cx: AsyncAppContext,
8299 ) -> Result<proto::OpenBufferForSymbolResponse> {
8300 let peer_id = envelope.original_sender_id()?;
8301 let symbol = envelope
8302 .payload
8303 .symbol
8304 .ok_or_else(|| anyhow!("invalid symbol"))?;
8305 let symbol = this
8306 .update(&mut cx, |this, _| this.deserialize_symbol(symbol))?
8307 .await?;
8308 let symbol = this.update(&mut cx, |this, _| {
8309 let signature = this.symbol_signature(&symbol.path);
8310 if signature == symbol.signature {
8311 Ok(symbol)
8312 } else {
8313 Err(anyhow!("invalid symbol signature"))
8314 }
8315 })??;
8316 let buffer = this
8317 .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))?
8318 .await?;
8319
8320 this.update(&mut cx, |this, cx| {
8321 let is_private = buffer
8322 .read(cx)
8323 .file()
8324 .map(|f| f.is_private())
8325 .unwrap_or_default();
8326 if is_private {
8327 Err(anyhow!(ErrorCode::UnsharedItem))
8328 } else {
8329 Ok(proto::OpenBufferForSymbolResponse {
8330 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
8331 })
8332 }
8333 })?
8334 }
8335
8336 fn symbol_signature(&self, project_path: &ProjectPath) -> [u8; 32] {
8337 let mut hasher = Sha256::new();
8338 hasher.update(project_path.worktree_id.to_proto().to_be_bytes());
8339 hasher.update(project_path.path.to_string_lossy().as_bytes());
8340 hasher.update(self.nonce.to_be_bytes());
8341 hasher.finalize().as_slice().try_into().unwrap()
8342 }
8343
8344 async fn handle_open_buffer_by_id(
8345 this: Model<Self>,
8346 envelope: TypedEnvelope<proto::OpenBufferById>,
8347 _: Arc<Client>,
8348 mut cx: AsyncAppContext,
8349 ) -> Result<proto::OpenBufferResponse> {
8350 let peer_id = envelope.original_sender_id()?;
8351 let buffer_id = BufferId::new(envelope.payload.id)?;
8352 let buffer = this
8353 .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
8354 .await?;
8355 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
8356 }
8357
8358 async fn handle_open_buffer_by_path(
8359 this: Model<Self>,
8360 envelope: TypedEnvelope<proto::OpenBufferByPath>,
8361 _: Arc<Client>,
8362 mut cx: AsyncAppContext,
8363 ) -> Result<proto::OpenBufferResponse> {
8364 let peer_id = envelope.original_sender_id()?;
8365 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
8366 let open_buffer = this.update(&mut cx, |this, cx| {
8367 this.open_buffer(
8368 ProjectPath {
8369 worktree_id,
8370 path: PathBuf::from(envelope.payload.path).into(),
8371 },
8372 cx,
8373 )
8374 })?;
8375
8376 let buffer = open_buffer.await?;
8377 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
8378 }
8379
8380 fn respond_to_open_buffer_request(
8381 this: Model<Self>,
8382 buffer: Model<Buffer>,
8383 peer_id: proto::PeerId,
8384 cx: &mut AsyncAppContext,
8385 ) -> Result<proto::OpenBufferResponse> {
8386 this.update(cx, |this, cx| {
8387 let is_private = buffer
8388 .read(cx)
8389 .file()
8390 .map(|f| f.is_private())
8391 .unwrap_or_default();
8392 if is_private {
8393 Err(anyhow!(ErrorCode::UnsharedItem))
8394 } else {
8395 Ok(proto::OpenBufferResponse {
8396 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
8397 })
8398 }
8399 })?
8400 }
8401
8402 fn serialize_project_transaction_for_peer(
8403 &mut self,
8404 project_transaction: ProjectTransaction,
8405 peer_id: proto::PeerId,
8406 cx: &mut AppContext,
8407 ) -> proto::ProjectTransaction {
8408 let mut serialized_transaction = proto::ProjectTransaction {
8409 buffer_ids: Default::default(),
8410 transactions: Default::default(),
8411 };
8412 for (buffer, transaction) in project_transaction.0 {
8413 serialized_transaction
8414 .buffer_ids
8415 .push(self.create_buffer_for_peer(&buffer, peer_id, cx).into());
8416 serialized_transaction
8417 .transactions
8418 .push(language::proto::serialize_transaction(&transaction));
8419 }
8420 serialized_transaction
8421 }
8422
8423 fn deserialize_project_transaction(
8424 &mut self,
8425 message: proto::ProjectTransaction,
8426 push_to_history: bool,
8427 cx: &mut ModelContext<Self>,
8428 ) -> Task<Result<ProjectTransaction>> {
8429 cx.spawn(move |this, mut cx| async move {
8430 let mut project_transaction = ProjectTransaction::default();
8431 for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions)
8432 {
8433 let buffer_id = BufferId::new(buffer_id)?;
8434 let buffer = this
8435 .update(&mut cx, |this, cx| {
8436 this.wait_for_remote_buffer(buffer_id, cx)
8437 })?
8438 .await?;
8439 let transaction = language::proto::deserialize_transaction(transaction)?;
8440 project_transaction.0.insert(buffer, transaction);
8441 }
8442
8443 for (buffer, transaction) in &project_transaction.0 {
8444 buffer
8445 .update(&mut cx, |buffer, _| {
8446 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
8447 })?
8448 .await?;
8449
8450 if push_to_history {
8451 buffer.update(&mut cx, |buffer, _| {
8452 buffer.push_transaction(transaction.clone(), Instant::now());
8453 })?;
8454 }
8455 }
8456
8457 Ok(project_transaction)
8458 })
8459 }
8460
8461 fn create_buffer_for_peer(
8462 &mut self,
8463 buffer: &Model<Buffer>,
8464 peer_id: proto::PeerId,
8465 cx: &mut AppContext,
8466 ) -> BufferId {
8467 let buffer_id = buffer.read(cx).remote_id();
8468 if let ProjectClientState::Shared { updates_tx, .. } = &self.client_state {
8469 updates_tx
8470 .unbounded_send(LocalProjectUpdate::CreateBufferForPeer { peer_id, buffer_id })
8471 .ok();
8472 }
8473 buffer_id
8474 }
8475
8476 fn wait_for_remote_buffer(
8477 &mut self,
8478 id: BufferId,
8479 cx: &mut ModelContext<Self>,
8480 ) -> Task<Result<Model<Buffer>>> {
8481 let mut opened_buffer_rx = self.opened_buffer.1.clone();
8482
8483 cx.spawn(move |this, mut cx| async move {
8484 let buffer = loop {
8485 let Some(this) = this.upgrade() else {
8486 return Err(anyhow!("project dropped"));
8487 };
8488
8489 let buffer = this.update(&mut cx, |this, _cx| {
8490 this.opened_buffers
8491 .get(&id)
8492 .and_then(|buffer| buffer.upgrade())
8493 })?;
8494
8495 if let Some(buffer) = buffer {
8496 break buffer;
8497 } else if this.update(&mut cx, |this, _| this.is_disconnected())? {
8498 return Err(anyhow!("disconnected before buffer {} could be opened", id));
8499 }
8500
8501 this.update(&mut cx, |this, _| {
8502 this.incomplete_remote_buffers.entry(id).or_default();
8503 })?;
8504 drop(this);
8505
8506 opened_buffer_rx
8507 .next()
8508 .await
8509 .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
8510 };
8511
8512 Ok(buffer)
8513 })
8514 }
8515
8516 fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
8517 let project_id = match self.client_state {
8518 ProjectClientState::Remote {
8519 sharing_has_stopped,
8520 remote_id,
8521 ..
8522 } => {
8523 if sharing_has_stopped {
8524 return Task::ready(Err(anyhow!(
8525 "can't synchronize remote buffers on a readonly project"
8526 )));
8527 } else {
8528 remote_id
8529 }
8530 }
8531 ProjectClientState::Shared { .. } | ProjectClientState::Local => {
8532 return Task::ready(Err(anyhow!(
8533 "can't synchronize remote buffers on a local project"
8534 )))
8535 }
8536 };
8537
8538 let client = self.client.clone();
8539 cx.spawn(move |this, mut cx| async move {
8540 let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
8541 let buffers = this
8542 .opened_buffers
8543 .iter()
8544 .filter_map(|(id, buffer)| {
8545 let buffer = buffer.upgrade()?;
8546 Some(proto::BufferVersion {
8547 id: (*id).into(),
8548 version: language::proto::serialize_version(&buffer.read(cx).version),
8549 })
8550 })
8551 .collect();
8552 let incomplete_buffer_ids = this
8553 .incomplete_remote_buffers
8554 .keys()
8555 .copied()
8556 .collect::<Vec<_>>();
8557
8558 (buffers, incomplete_buffer_ids)
8559 })?;
8560 let response = client
8561 .request(proto::SynchronizeBuffers {
8562 project_id,
8563 buffers,
8564 })
8565 .await?;
8566
8567 let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
8568 response
8569 .buffers
8570 .into_iter()
8571 .map(|buffer| {
8572 let client = client.clone();
8573 let buffer_id = match BufferId::new(buffer.id) {
8574 Ok(id) => id,
8575 Err(e) => {
8576 return Task::ready(Err(e));
8577 }
8578 };
8579 let remote_version = language::proto::deserialize_version(&buffer.version);
8580 if let Some(buffer) = this.buffer_for_id(buffer_id) {
8581 let operations =
8582 buffer.read(cx).serialize_ops(Some(remote_version), cx);
8583 cx.background_executor().spawn(async move {
8584 let operations = operations.await;
8585 for chunk in split_operations(operations) {
8586 client
8587 .request(proto::UpdateBuffer {
8588 project_id,
8589 buffer_id: buffer_id.into(),
8590 operations: chunk,
8591 })
8592 .await?;
8593 }
8594 anyhow::Ok(())
8595 })
8596 } else {
8597 Task::ready(Ok(()))
8598 }
8599 })
8600 .collect::<Vec<_>>()
8601 })?;
8602
8603 // Any incomplete buffers have open requests waiting. Request that the host sends
8604 // creates these buffers for us again to unblock any waiting futures.
8605 for id in incomplete_buffer_ids {
8606 cx.background_executor()
8607 .spawn(client.request(proto::OpenBufferById {
8608 project_id,
8609 id: id.into(),
8610 }))
8611 .detach();
8612 }
8613
8614 futures::future::join_all(send_updates_for_buffers)
8615 .await
8616 .into_iter()
8617 .collect()
8618 })
8619 }
8620
8621 pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
8622 self.worktrees()
8623 .map(|worktree| {
8624 let worktree = worktree.read(cx);
8625 proto::WorktreeMetadata {
8626 id: worktree.id().to_proto(),
8627 root_name: worktree.root_name().into(),
8628 visible: worktree.is_visible(),
8629 abs_path: worktree.abs_path().to_string_lossy().into(),
8630 }
8631 })
8632 .collect()
8633 }
8634
8635 fn set_worktrees_from_proto(
8636 &mut self,
8637 worktrees: Vec<proto::WorktreeMetadata>,
8638 cx: &mut ModelContext<Project>,
8639 ) -> Result<()> {
8640 let replica_id = self.replica_id();
8641 let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
8642
8643 let mut old_worktrees_by_id = self
8644 .worktrees
8645 .drain(..)
8646 .filter_map(|worktree| {
8647 let worktree = worktree.upgrade()?;
8648 Some((worktree.read(cx).id(), worktree))
8649 })
8650 .collect::<HashMap<_, _>>();
8651
8652 for worktree in worktrees {
8653 if let Some(old_worktree) =
8654 old_worktrees_by_id.remove(&WorktreeId::from_proto(worktree.id))
8655 {
8656 self.worktrees.push(WorktreeHandle::Strong(old_worktree));
8657 } else {
8658 let worktree =
8659 Worktree::remote(remote_id, replica_id, worktree, self.client.clone(), cx);
8660 let _ = self.add_worktree(&worktree, cx);
8661 }
8662 }
8663
8664 self.metadata_changed(cx);
8665 for id in old_worktrees_by_id.keys() {
8666 cx.emit(Event::WorktreeRemoved(*id));
8667 }
8668
8669 Ok(())
8670 }
8671
8672 fn set_collaborators_from_proto(
8673 &mut self,
8674 messages: Vec<proto::Collaborator>,
8675 cx: &mut ModelContext<Self>,
8676 ) -> Result<()> {
8677 let mut collaborators = HashMap::default();
8678 for message in messages {
8679 let collaborator = Collaborator::from_proto(message)?;
8680 collaborators.insert(collaborator.peer_id, collaborator);
8681 }
8682 for old_peer_id in self.collaborators.keys() {
8683 if !collaborators.contains_key(old_peer_id) {
8684 cx.emit(Event::CollaboratorLeft(*old_peer_id));
8685 }
8686 }
8687 self.collaborators = collaborators;
8688 Ok(())
8689 }
8690
8691 fn deserialize_symbol(
8692 &self,
8693 serialized_symbol: proto::Symbol,
8694 ) -> impl Future<Output = Result<Symbol>> {
8695 let languages = self.languages.clone();
8696 async move {
8697 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
8698 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
8699 let start = serialized_symbol
8700 .start
8701 .ok_or_else(|| anyhow!("invalid start"))?;
8702 let end = serialized_symbol
8703 .end
8704 .ok_or_else(|| anyhow!("invalid end"))?;
8705 let kind = unsafe { mem::transmute(serialized_symbol.kind) };
8706 let path = ProjectPath {
8707 worktree_id,
8708 path: PathBuf::from(serialized_symbol.path).into(),
8709 };
8710 let language = languages
8711 .language_for_file(&path.path, None)
8712 .await
8713 .log_err();
8714 Ok(Symbol {
8715 language_server_name: LanguageServerName(
8716 serialized_symbol.language_server_name.into(),
8717 ),
8718 source_worktree_id,
8719 path,
8720 label: {
8721 match language {
8722 Some(language) => {
8723 language
8724 .label_for_symbol(&serialized_symbol.name, kind)
8725 .await
8726 }
8727 None => None,
8728 }
8729 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None))
8730 },
8731
8732 name: serialized_symbol.name,
8733 range: Unclipped(PointUtf16::new(start.row, start.column))
8734 ..Unclipped(PointUtf16::new(end.row, end.column)),
8735 kind,
8736 signature: serialized_symbol
8737 .signature
8738 .try_into()
8739 .map_err(|_| anyhow!("invalid signature"))?,
8740 })
8741 }
8742 }
8743
8744 async fn handle_buffer_saved(
8745 this: Model<Self>,
8746 envelope: TypedEnvelope<proto::BufferSaved>,
8747 _: Arc<Client>,
8748 mut cx: AsyncAppContext,
8749 ) -> Result<()> {
8750 let fingerprint = deserialize_fingerprint(&envelope.payload.fingerprint)?;
8751 let version = deserialize_version(&envelope.payload.version);
8752 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8753 let mtime = envelope
8754 .payload
8755 .mtime
8756 .ok_or_else(|| anyhow!("missing mtime"))?
8757 .into();
8758
8759 this.update(&mut cx, |this, cx| {
8760 let buffer = this
8761 .opened_buffers
8762 .get(&buffer_id)
8763 .and_then(|buffer| buffer.upgrade())
8764 .or_else(|| {
8765 this.incomplete_remote_buffers
8766 .get(&buffer_id)
8767 .and_then(|b| b.clone())
8768 });
8769 if let Some(buffer) = buffer {
8770 buffer.update(cx, |buffer, cx| {
8771 buffer.did_save(version, fingerprint, mtime, cx);
8772 });
8773 }
8774 Ok(())
8775 })?
8776 }
8777
8778 async fn handle_buffer_reloaded(
8779 this: Model<Self>,
8780 envelope: TypedEnvelope<proto::BufferReloaded>,
8781 _: Arc<Client>,
8782 mut cx: AsyncAppContext,
8783 ) -> Result<()> {
8784 let payload = envelope.payload;
8785 let version = deserialize_version(&payload.version);
8786 let fingerprint = deserialize_fingerprint(&payload.fingerprint)?;
8787 let line_ending = deserialize_line_ending(
8788 proto::LineEnding::from_i32(payload.line_ending)
8789 .ok_or_else(|| anyhow!("missing line ending"))?,
8790 );
8791 let mtime = payload
8792 .mtime
8793 .ok_or_else(|| anyhow!("missing mtime"))?
8794 .into();
8795 let buffer_id = BufferId::new(payload.buffer_id)?;
8796 this.update(&mut cx, |this, cx| {
8797 let buffer = this
8798 .opened_buffers
8799 .get(&buffer_id)
8800 .and_then(|buffer| buffer.upgrade())
8801 .or_else(|| {
8802 this.incomplete_remote_buffers
8803 .get(&buffer_id)
8804 .cloned()
8805 .flatten()
8806 });
8807 if let Some(buffer) = buffer {
8808 buffer.update(cx, |buffer, cx| {
8809 buffer.did_reload(version, fingerprint, line_ending, mtime, cx);
8810 });
8811 }
8812 Ok(())
8813 })?
8814 }
8815
8816 #[allow(clippy::type_complexity)]
8817 fn edits_from_lsp(
8818 &mut self,
8819 buffer: &Model<Buffer>,
8820 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
8821 server_id: LanguageServerId,
8822 version: Option<i32>,
8823 cx: &mut ModelContext<Self>,
8824 ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
8825 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
8826 cx.background_executor().spawn(async move {
8827 let snapshot = snapshot?;
8828 let mut lsp_edits = lsp_edits
8829 .into_iter()
8830 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
8831 .collect::<Vec<_>>();
8832 lsp_edits.sort_by_key(|(range, _)| range.start);
8833
8834 let mut lsp_edits = lsp_edits.into_iter().peekable();
8835 let mut edits = Vec::new();
8836 while let Some((range, mut new_text)) = lsp_edits.next() {
8837 // Clip invalid ranges provided by the language server.
8838 let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
8839 ..snapshot.clip_point_utf16(range.end, Bias::Left);
8840
8841 // Combine any LSP edits that are adjacent.
8842 //
8843 // Also, combine LSP edits that are separated from each other by only
8844 // a newline. This is important because for some code actions,
8845 // Rust-analyzer rewrites the entire buffer via a series of edits that
8846 // are separated by unchanged newline characters.
8847 //
8848 // In order for the diffing logic below to work properly, any edits that
8849 // cancel each other out must be combined into one.
8850 while let Some((next_range, next_text)) = lsp_edits.peek() {
8851 if next_range.start.0 > range.end {
8852 if next_range.start.0.row > range.end.row + 1
8853 || next_range.start.0.column > 0
8854 || snapshot.clip_point_utf16(
8855 Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
8856 Bias::Left,
8857 ) > range.end
8858 {
8859 break;
8860 }
8861 new_text.push('\n');
8862 }
8863 range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
8864 new_text.push_str(next_text);
8865 lsp_edits.next();
8866 }
8867
8868 // For multiline edits, perform a diff of the old and new text so that
8869 // we can identify the changes more precisely, preserving the locations
8870 // of any anchors positioned in the unchanged regions.
8871 if range.end.row > range.start.row {
8872 let mut offset = range.start.to_offset(&snapshot);
8873 let old_text = snapshot.text_for_range(range).collect::<String>();
8874
8875 let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
8876 let mut moved_since_edit = true;
8877 for change in diff.iter_all_changes() {
8878 let tag = change.tag();
8879 let value = change.value();
8880 match tag {
8881 ChangeTag::Equal => {
8882 offset += value.len();
8883 moved_since_edit = true;
8884 }
8885 ChangeTag::Delete => {
8886 let start = snapshot.anchor_after(offset);
8887 let end = snapshot.anchor_before(offset + value.len());
8888 if moved_since_edit {
8889 edits.push((start..end, String::new()));
8890 } else {
8891 edits.last_mut().unwrap().0.end = end;
8892 }
8893 offset += value.len();
8894 moved_since_edit = false;
8895 }
8896 ChangeTag::Insert => {
8897 if moved_since_edit {
8898 let anchor = snapshot.anchor_after(offset);
8899 edits.push((anchor..anchor, value.to_string()));
8900 } else {
8901 edits.last_mut().unwrap().1.push_str(value);
8902 }
8903 moved_since_edit = false;
8904 }
8905 }
8906 }
8907 } else if range.end == range.start {
8908 let anchor = snapshot.anchor_after(range.start);
8909 edits.push((anchor..anchor, new_text));
8910 } else {
8911 let edit_start = snapshot.anchor_after(range.start);
8912 let edit_end = snapshot.anchor_before(range.end);
8913 edits.push((edit_start..edit_end, new_text));
8914 }
8915 }
8916
8917 Ok(edits)
8918 })
8919 }
8920
8921 fn buffer_snapshot_for_lsp_version(
8922 &mut self,
8923 buffer: &Model<Buffer>,
8924 server_id: LanguageServerId,
8925 version: Option<i32>,
8926 cx: &AppContext,
8927 ) -> Result<TextBufferSnapshot> {
8928 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
8929
8930 if let Some(version) = version {
8931 let buffer_id = buffer.read(cx).remote_id();
8932 let snapshots = self
8933 .buffer_snapshots
8934 .get_mut(&buffer_id)
8935 .and_then(|m| m.get_mut(&server_id))
8936 .ok_or_else(|| {
8937 anyhow!("no snapshots found for buffer {buffer_id} and server {server_id}")
8938 })?;
8939
8940 let found_snapshot = snapshots
8941 .binary_search_by_key(&version, |e| e.version)
8942 .map(|ix| snapshots[ix].snapshot.clone())
8943 .map_err(|_| {
8944 anyhow!("snapshot not found for buffer {buffer_id} server {server_id} at version {version}")
8945 })?;
8946
8947 snapshots.retain(|snapshot| snapshot.version + OLD_VERSIONS_TO_RETAIN >= version);
8948 Ok(found_snapshot)
8949 } else {
8950 Ok((buffer.read(cx)).text_snapshot())
8951 }
8952 }
8953
8954 pub fn language_servers(
8955 &self,
8956 ) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
8957 self.language_server_ids
8958 .iter()
8959 .map(|((worktree_id, server_name), server_id)| {
8960 (*server_id, server_name.clone(), *worktree_id)
8961 })
8962 }
8963
8964 pub fn supplementary_language_servers(
8965 &self,
8966 ) -> impl '_
8967 + Iterator<
8968 Item = (
8969 &LanguageServerId,
8970 &(LanguageServerName, Arc<LanguageServer>),
8971 ),
8972 > {
8973 self.supplementary_language_servers.iter()
8974 }
8975
8976 pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
8977 if let Some(LanguageServerState::Running { server, .. }) = self.language_servers.get(&id) {
8978 Some(server.clone())
8979 } else if let Some((_, server)) = self.supplementary_language_servers.get(&id) {
8980 Some(Arc::clone(server))
8981 } else {
8982 None
8983 }
8984 }
8985
8986 pub fn language_servers_for_buffer(
8987 &self,
8988 buffer: &Buffer,
8989 cx: &AppContext,
8990 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
8991 self.language_server_ids_for_buffer(buffer, cx)
8992 .into_iter()
8993 .filter_map(|server_id| match self.language_servers.get(&server_id)? {
8994 LanguageServerState::Running {
8995 adapter, server, ..
8996 } => Some((adapter, server)),
8997 _ => None,
8998 })
8999 }
9000
9001 fn primary_language_server_for_buffer(
9002 &self,
9003 buffer: &Buffer,
9004 cx: &AppContext,
9005 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
9006 self.language_servers_for_buffer(buffer, cx).next()
9007 }
9008
9009 pub fn language_server_for_buffer(
9010 &self,
9011 buffer: &Buffer,
9012 server_id: LanguageServerId,
9013 cx: &AppContext,
9014 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
9015 self.language_servers_for_buffer(buffer, cx)
9016 .find(|(_, s)| s.server_id() == server_id)
9017 }
9018
9019 fn language_server_ids_for_buffer(
9020 &self,
9021 buffer: &Buffer,
9022 cx: &AppContext,
9023 ) -> Vec<LanguageServerId> {
9024 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
9025 let worktree_id = file.worktree_id(cx);
9026 language
9027 .lsp_adapters()
9028 .iter()
9029 .flat_map(|adapter| {
9030 let key = (worktree_id, adapter.name.clone());
9031 self.language_server_ids.get(&key).copied()
9032 })
9033 .collect()
9034 } else {
9035 Vec::new()
9036 }
9037 }
9038}
9039
9040fn subscribe_for_copilot_events(
9041 copilot: &Model<Copilot>,
9042 cx: &mut ModelContext<'_, Project>,
9043) -> gpui::Subscription {
9044 cx.subscribe(
9045 copilot,
9046 |project, copilot, copilot_event, cx| match copilot_event {
9047 copilot::Event::CopilotLanguageServerStarted => {
9048 match copilot.read(cx).language_server() {
9049 Some((name, copilot_server)) => {
9050 // Another event wants to re-add the server that was already added and subscribed to, avoid doing it again.
9051 if !copilot_server.has_notification_handler::<copilot::request::LogMessage>() {
9052 let new_server_id = copilot_server.server_id();
9053 let weak_project = cx.weak_model();
9054 let copilot_log_subscription = copilot_server
9055 .on_notification::<copilot::request::LogMessage, _>(
9056 move |params, mut cx| {
9057 weak_project.update(&mut cx, |_, cx| {
9058 cx.emit(Event::LanguageServerLog(
9059 new_server_id,
9060 params.message,
9061 ));
9062 }).ok();
9063 },
9064 );
9065 project.supplementary_language_servers.insert(new_server_id, (name.clone(), Arc::clone(copilot_server)));
9066 project.copilot_log_subscription = Some(copilot_log_subscription);
9067 cx.emit(Event::LanguageServerAdded(new_server_id));
9068 }
9069 }
9070 None => debug_panic!("Received Copilot language server started event, but no language server is running"),
9071 }
9072 }
9073 },
9074 )
9075}
9076
9077fn glob_literal_prefix<'a>(glob: &'a str) -> &'a str {
9078 let mut literal_end = 0;
9079 for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() {
9080 if part.contains(&['*', '?', '{', '}']) {
9081 break;
9082 } else {
9083 if i > 0 {
9084 // Account for separator prior to this part
9085 literal_end += path::MAIN_SEPARATOR.len_utf8();
9086 }
9087 literal_end += part.len();
9088 }
9089 }
9090 &glob[..literal_end]
9091}
9092
9093impl WorktreeHandle {
9094 pub fn upgrade(&self) -> Option<Model<Worktree>> {
9095 match self {
9096 WorktreeHandle::Strong(handle) => Some(handle.clone()),
9097 WorktreeHandle::Weak(handle) => handle.upgrade(),
9098 }
9099 }
9100
9101 pub fn handle_id(&self) -> usize {
9102 match self {
9103 WorktreeHandle::Strong(handle) => handle.entity_id().as_u64() as usize,
9104 WorktreeHandle::Weak(handle) => handle.entity_id().as_u64() as usize,
9105 }
9106 }
9107}
9108
9109impl OpenBuffer {
9110 pub fn upgrade(&self) -> Option<Model<Buffer>> {
9111 match self {
9112 OpenBuffer::Strong(handle) => Some(handle.clone()),
9113 OpenBuffer::Weak(handle) => handle.upgrade(),
9114 OpenBuffer::Operations(_) => None,
9115 }
9116 }
9117}
9118
9119pub struct PathMatchCandidateSet {
9120 pub snapshot: Snapshot,
9121 pub include_ignored: bool,
9122 pub include_root_name: bool,
9123}
9124
9125impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
9126 type Candidates = PathMatchCandidateSetIter<'a>;
9127
9128 fn id(&self) -> usize {
9129 self.snapshot.id().to_usize()
9130 }
9131
9132 fn len(&self) -> usize {
9133 if self.include_ignored {
9134 self.snapshot.file_count()
9135 } else {
9136 self.snapshot.visible_file_count()
9137 }
9138 }
9139
9140 fn prefix(&self) -> Arc<str> {
9141 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
9142 self.snapshot.root_name().into()
9143 } else if self.include_root_name {
9144 format!("{}/", self.snapshot.root_name()).into()
9145 } else {
9146 "".into()
9147 }
9148 }
9149
9150 fn candidates(&'a self, start: usize) -> Self::Candidates {
9151 PathMatchCandidateSetIter {
9152 traversal: self.snapshot.files(self.include_ignored, start),
9153 }
9154 }
9155}
9156
9157pub struct PathMatchCandidateSetIter<'a> {
9158 traversal: Traversal<'a>,
9159}
9160
9161impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
9162 type Item = fuzzy::PathMatchCandidate<'a>;
9163
9164 fn next(&mut self) -> Option<Self::Item> {
9165 self.traversal.next().map(|entry| {
9166 if let EntryKind::File(char_bag) = entry.kind {
9167 fuzzy::PathMatchCandidate {
9168 path: &entry.path,
9169 char_bag,
9170 }
9171 } else {
9172 unreachable!()
9173 }
9174 })
9175 }
9176}
9177
9178impl EventEmitter<Event> for Project {}
9179
9180impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
9181 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
9182 Self {
9183 worktree_id,
9184 path: path.as_ref().into(),
9185 }
9186 }
9187}
9188
9189struct ProjectLspAdapterDelegate {
9190 project: Model<Project>,
9191 worktree: Model<Worktree>,
9192 http_client: Arc<dyn HttpClient>,
9193}
9194
9195impl ProjectLspAdapterDelegate {
9196 fn new(project: &Project, worktree: &Model<Worktree>, cx: &ModelContext<Project>) -> Arc<Self> {
9197 Arc::new(Self {
9198 project: cx.handle(),
9199 worktree: worktree.clone(),
9200 http_client: project.client.http_client(),
9201 })
9202 }
9203}
9204
9205impl LspAdapterDelegate for ProjectLspAdapterDelegate {
9206 fn show_notification(&self, message: &str, cx: &mut AppContext) {
9207 self.project
9208 .update(cx, |_, cx| cx.emit(Event::Notification(message.to_owned())));
9209 }
9210
9211 fn http_client(&self) -> Arc<dyn HttpClient> {
9212 self.http_client.clone()
9213 }
9214
9215 fn which_command(
9216 &self,
9217 command: OsString,
9218 cx: &AppContext,
9219 ) -> Task<Option<(PathBuf, HashMap<String, String>)>> {
9220 let worktree_abs_path = self.worktree.read(cx).abs_path();
9221 let command = command.to_owned();
9222
9223 cx.background_executor().spawn(async move {
9224 let shell_env = load_shell_environment(&worktree_abs_path)
9225 .await
9226 .with_context(|| {
9227 format!(
9228 "failed to determine load login shell environment in {worktree_abs_path:?}"
9229 )
9230 })
9231 .log_err();
9232
9233 if let Some(shell_env) = shell_env {
9234 let shell_path = shell_env.get("PATH");
9235 match which::which_in(&command, shell_path, &worktree_abs_path) {
9236 Ok(command_path) => Some((command_path, shell_env)),
9237 Err(error) => {
9238 log::warn!(
9239 "failed to determine path for command {:?} in shell PATH {:?}: {error}",
9240 command.to_string_lossy(),
9241 shell_path.map(String::as_str).unwrap_or("")
9242 );
9243 None
9244 }
9245 }
9246 } else {
9247 None
9248 }
9249 })
9250 }
9251}
9252
9253fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
9254 proto::Symbol {
9255 language_server_name: symbol.language_server_name.0.to_string(),
9256 source_worktree_id: symbol.source_worktree_id.to_proto(),
9257 worktree_id: symbol.path.worktree_id.to_proto(),
9258 path: symbol.path.path.to_string_lossy().to_string(),
9259 name: symbol.name.clone(),
9260 kind: unsafe { mem::transmute(symbol.kind) },
9261 start: Some(proto::PointUtf16 {
9262 row: symbol.range.start.0.row,
9263 column: symbol.range.start.0.column,
9264 }),
9265 end: Some(proto::PointUtf16 {
9266 row: symbol.range.end.0.row,
9267 column: symbol.range.end.0.column,
9268 }),
9269 signature: symbol.signature.to_vec(),
9270 }
9271}
9272
9273fn relativize_path(base: &Path, path: &Path) -> PathBuf {
9274 let mut path_components = path.components();
9275 let mut base_components = base.components();
9276 let mut components: Vec<Component> = Vec::new();
9277 loop {
9278 match (path_components.next(), base_components.next()) {
9279 (None, None) => break,
9280 (Some(a), None) => {
9281 components.push(a);
9282 components.extend(path_components.by_ref());
9283 break;
9284 }
9285 (None, _) => components.push(Component::ParentDir),
9286 (Some(a), Some(b)) if components.is_empty() && a == b => (),
9287 (Some(a), Some(b)) if b == Component::CurDir => components.push(a),
9288 (Some(a), Some(_)) => {
9289 components.push(Component::ParentDir);
9290 for _ in base_components {
9291 components.push(Component::ParentDir);
9292 }
9293 components.push(a);
9294 components.extend(path_components.by_ref());
9295 break;
9296 }
9297 }
9298 }
9299 components.iter().map(|c| c.as_os_str()).collect()
9300}
9301
9302fn resolve_path(base: &Path, path: &Path) -> PathBuf {
9303 let mut result = base.to_path_buf();
9304 for component in path.components() {
9305 match component {
9306 Component::ParentDir => {
9307 result.pop();
9308 }
9309 Component::CurDir => (),
9310 _ => result.push(component),
9311 }
9312 }
9313 result
9314}
9315
9316impl Item for Buffer {
9317 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
9318 File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
9319 }
9320
9321 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
9322 File::from_dyn(self.file()).map(|file| ProjectPath {
9323 worktree_id: file.worktree_id(cx),
9324 path: file.path().clone(),
9325 })
9326 }
9327}
9328
9329async fn wait_for_loading_buffer(
9330 mut receiver: postage::watch::Receiver<Option<Result<Model<Buffer>, Arc<anyhow::Error>>>>,
9331) -> Result<Model<Buffer>, Arc<anyhow::Error>> {
9332 loop {
9333 if let Some(result) = receiver.borrow().as_ref() {
9334 match result {
9335 Ok(buffer) => return Ok(buffer.to_owned()),
9336 Err(e) => return Err(e.to_owned()),
9337 }
9338 }
9339 receiver.next().await;
9340 }
9341}
9342
9343fn include_text(server: &lsp::LanguageServer) -> bool {
9344 server
9345 .capabilities()
9346 .text_document_sync
9347 .as_ref()
9348 .and_then(|sync| match sync {
9349 lsp::TextDocumentSyncCapability::Kind(_) => None,
9350 lsp::TextDocumentSyncCapability::Options(options) => options.save.as_ref(),
9351 })
9352 .and_then(|save_options| match save_options {
9353 lsp::TextDocumentSyncSaveOptions::Supported(_) => None,
9354 lsp::TextDocumentSyncSaveOptions::SaveOptions(options) => options.include_text,
9355 })
9356 .unwrap_or(false)
9357}
9358
9359async fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
9360 let marker = "ZED_SHELL_START";
9361 let shell = env::var("SHELL").context(
9362 "SHELL environment variable is not assigned so we can't source login environment variables",
9363 )?;
9364 let output = smol::process::Command::new(&shell)
9365 .args([
9366 "-i",
9367 "-c",
9368 // What we're doing here is to spawn a shell and then `cd` into
9369 // the project directory to get the env in there as if the user
9370 // `cd`'d into it. We do that because tools like direnv, asdf, ...
9371 // hook into `cd` and only set up the env after that.
9372 //
9373 // The `exit 0` is the result of hours of debugging, trying to find out
9374 // why running this command here, without `exit 0`, would mess
9375 // up signal process for our process so that `ctrl-c` doesn't work
9376 // anymore.
9377 // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
9378 // do that, but it does, and `exit 0` helps.
9379 &format!("cd {dir:?}; echo {marker}; /usr/bin/env -0; exit 0;"),
9380 ])
9381 .output()
9382 .await
9383 .context("failed to spawn login shell to source login environment variables")?;
9384
9385 anyhow::ensure!(
9386 output.status.success(),
9387 "login shell exited with error {:?}",
9388 output.status
9389 );
9390
9391 let stdout = String::from_utf8_lossy(&output.stdout);
9392 let env_output_start = stdout.find(marker).ok_or_else(|| {
9393 anyhow!(
9394 "failed to parse output of `env` command in login shell: {}",
9395 stdout
9396 )
9397 })?;
9398
9399 let mut parsed_env = HashMap::default();
9400 let env_output = &stdout[env_output_start + marker.len()..];
9401 for line in env_output.split_terminator('\0') {
9402 if let Some(separator_index) = line.find('=') {
9403 let key = line[..separator_index].to_string();
9404 let value = line[separator_index + 1..].to_string();
9405 parsed_env.insert(key, value);
9406 }
9407 }
9408 Ok(parsed_env)
9409}