1pub mod buffer_store;
2pub mod connection_manager;
3pub mod debounced_delay;
4pub mod lsp_command;
5pub mod lsp_ext_command;
6mod prettier_support;
7pub mod project_settings;
8pub mod search;
9mod task_inventory;
10pub mod terminals;
11pub mod worktree_store;
12
13#[cfg(test)]
14mod project_tests;
15
16pub mod search_history;
17mod yarn;
18
19use anyhow::{anyhow, bail, Context as _, Result};
20use async_trait::async_trait;
21use buffer_store::{BufferStore, BufferStoreEvent};
22use client::{
23 proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId,
24 TypedEnvelope, UserStore,
25};
26use clock::ReplicaId;
27use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet};
28use debounced_delay::DebouncedDelay;
29use futures::{
30 channel::mpsc::{self, UnboundedReceiver},
31 future::{join_all, try_join_all, Shared},
32 select,
33 stream::FuturesUnordered,
34 AsyncWriteExt, Future, FutureExt, StreamExt,
35};
36
37use git::{blame::Blame, repository::GitRepository};
38use globset::{Glob, GlobSet, GlobSetBuilder};
39use gpui::{
40 AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context, Entity, EventEmitter, Model,
41 ModelContext, PromptLevel, SharedString, Task, WeakModel, WindowContext,
42};
43use http_client::HttpClient;
44use itertools::Itertools;
45use language::{
46 language_settings::{
47 language_settings, AllLanguageSettings, FormatOnSave, Formatter, InlayHintKind,
48 LanguageSettings, SelectedFormatter,
49 },
50 markdown, point_to_lsp, prepare_completion_documentation,
51 proto::{
52 deserialize_anchor, deserialize_version, serialize_anchor, serialize_line_ending,
53 serialize_version, split_operations,
54 },
55 range_from_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability, CodeLabel,
56 ContextProvider, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Documentation,
57 Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
58 LspAdapterDelegate, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset,
59 ToPointUtf16, Transaction, Unclipped,
60};
61use log::error;
62use lsp::{
63 CompletionContext, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
64 DocumentHighlightKind, Edit, FileSystemWatcher, InsertTextFormat, LanguageServer,
65 LanguageServerBinary, LanguageServerId, LspRequestFuture, MessageActionItem, MessageType,
66 OneOf, ServerHealthStatus, ServerStatus, TextEdit, WorkDoneProgressCancelParams,
67};
68use lsp_command::*;
69use node_runtime::NodeRuntime;
70use parking_lot::{Mutex, RwLock};
71use paths::{
72 local_settings_file_relative_path, local_tasks_file_relative_path,
73 local_vscode_tasks_file_relative_path,
74};
75use postage::watch;
76use prettier_support::{DefaultPrettier, PrettierInstance};
77use project_settings::{DirenvSettings, LspSettings, ProjectSettings};
78use rand::prelude::*;
79use remote::SshSession;
80use rpc::{
81 proto::{AddWorktree, AnyProtoClient},
82 ErrorCode,
83};
84use search::{SearchQuery, SearchResult};
85use search_history::SearchHistory;
86use serde::Serialize;
87use settings::{watch_config_file, Settings, SettingsLocation, SettingsStore};
88use sha2::{Digest, Sha256};
89use similar::{ChangeTag, TextDiff};
90use smol::channel::{Receiver, Sender};
91use snippet::Snippet;
92use snippet_provider::SnippetProvider;
93use std::{
94 borrow::Cow,
95 cell::RefCell,
96 cmp::Ordering,
97 convert::TryInto,
98 env,
99 ffi::OsStr,
100 hash::Hash,
101 iter, mem,
102 ops::Range,
103 path::{self, Component, Path, PathBuf},
104 process::Stdio,
105 str,
106 sync::{
107 atomic::{AtomicUsize, Ordering::SeqCst},
108 Arc,
109 },
110 time::{Duration, Instant},
111};
112use task::{
113 static_source::{StaticSource, TrackedFile},
114 HideStrategy, RevealStrategy, Shell, TaskContext, TaskTemplate, TaskVariables, VariableName,
115};
116use terminals::Terminals;
117use text::{Anchor, BufferId, LineEnding};
118use util::{
119 debug_panic, defer, maybe, merge_json_value_into, parse_env_output, paths::compare_paths,
120 post_inc, ResultExt, TryFutureExt as _,
121};
122use worktree::{CreatedEntry, Snapshot, Traversal};
123use worktree_store::{WorktreeStore, WorktreeStoreEvent};
124use yarn::YarnPathStore;
125
126pub use fs::*;
127pub use language::Location;
128#[cfg(any(test, feature = "test-support"))]
129pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
130pub use task_inventory::{
131 BasicContextProvider, ContextProviderWithTasks, Inventory, TaskSourceKind,
132};
133pub use worktree::{
134 Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, RepositoryEntry,
135 UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
136 FS_WATCH_LATENCY,
137};
138
139const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
140const SERVER_REINSTALL_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
141const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
142pub const SERVER_PROGRESS_THROTTLE_TIMEOUT: Duration = Duration::from_millis(100);
143
144const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
145const MAX_SEARCH_RESULT_FILES: usize = 5_000;
146const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
147
148pub trait Item {
149 fn try_open(
150 project: &Model<Project>,
151 path: &ProjectPath,
152 cx: &mut AppContext,
153 ) -> Option<Task<Result<Model<Self>>>>
154 where
155 Self: Sized;
156 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
157 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
158}
159
160#[derive(Clone)]
161pub enum OpenedBufferEvent {
162 Disconnected,
163 Ok(BufferId),
164 Err(BufferId, Arc<anyhow::Error>),
165}
166
167/// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files.
168/// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly.
169/// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.
170///
171/// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).
172pub struct Project {
173 active_entry: Option<ProjectEntryId>,
174 buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
175 languages: Arc<LanguageRegistry>,
176 supplementary_language_servers:
177 HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
178 language_servers: HashMap<LanguageServerId, LanguageServerState>,
179 language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
180 language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
181 last_formatting_failure: Option<String>,
182 last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
183 language_server_watched_paths: HashMap<LanguageServerId, HashMap<WorktreeId, GlobSet>>,
184 language_server_watcher_registrations:
185 HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
186 client: Arc<client::Client>,
187 next_entry_id: Arc<AtomicUsize>,
188 join_project_response_message_id: u32,
189 next_diagnostic_group_id: usize,
190 diagnostic_summaries:
191 HashMap<WorktreeId, HashMap<Arc<Path>, HashMap<LanguageServerId, DiagnosticSummary>>>,
192 diagnostics: HashMap<
193 WorktreeId,
194 HashMap<
195 Arc<Path>,
196 Vec<(
197 LanguageServerId,
198 Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
199 )>,
200 >,
201 >,
202 user_store: Model<UserStore>,
203 fs: Arc<dyn Fs>,
204 ssh_session: Option<Arc<SshSession>>,
205 client_state: ProjectClientState,
206 collaborators: HashMap<proto::PeerId, Collaborator>,
207 client_subscriptions: Vec<client::Subscription>,
208 worktree_store: Model<WorktreeStore>,
209 buffer_store: Model<BufferStore>,
210 _subscriptions: Vec<gpui::Subscription>,
211 shared_buffers: HashMap<proto::PeerId, HashSet<BufferId>>,
212 #[allow(clippy::type_complexity)]
213 loading_worktrees:
214 HashMap<Arc<Path>, Shared<Task<Result<Model<Worktree>, Arc<anyhow::Error>>>>>,
215 buffer_snapshots: HashMap<BufferId, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots
216 buffers_being_formatted: HashSet<BufferId>,
217 buffers_needing_diff: HashSet<WeakModel<Buffer>>,
218 git_diff_debouncer: DebouncedDelay<Self>,
219 remotely_created_buffers: Arc<Mutex<RemotelyCreatedBuffers>>,
220 nonce: u128,
221 _maintain_buffer_languages: Task<()>,
222 _maintain_workspace_config: Task<Result<()>>,
223 terminals: Terminals,
224 current_lsp_settings: HashMap<Arc<str>, LspSettings>,
225 node: Option<Arc<dyn NodeRuntime>>,
226 default_prettier: DefaultPrettier,
227 prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
228 prettier_instances: HashMap<PathBuf, PrettierInstance>,
229 tasks: Model<Inventory>,
230 hosted_project_id: Option<ProjectId>,
231 dev_server_project_id: Option<client::DevServerProjectId>,
232 search_history: SearchHistory,
233 snippets: Model<SnippetProvider>,
234 yarn: Model<YarnPathStore>,
235 cached_shell_environments: HashMap<WorktreeId, HashMap<String, String>>,
236}
237
238#[derive(Default)]
239struct RemotelyCreatedBuffers {
240 buffers: Vec<Model<Buffer>>,
241 retain_count: usize,
242}
243
244struct RemotelyCreatedBufferGuard {
245 remote_buffers: std::sync::Weak<Mutex<RemotelyCreatedBuffers>>,
246}
247
248impl Drop for RemotelyCreatedBufferGuard {
249 fn drop(&mut self) {
250 if let Some(remote_buffers) = self.remote_buffers.upgrade() {
251 let mut remote_buffers = remote_buffers.lock();
252 assert!(
253 remote_buffers.retain_count > 0,
254 "RemotelyCreatedBufferGuard dropped too many times"
255 );
256 remote_buffers.retain_count -= 1;
257 if remote_buffers.retain_count == 0 {
258 remote_buffers.buffers.clear();
259 }
260 }
261 }
262}
263
264#[derive(Debug)]
265pub enum LanguageServerToQuery {
266 Primary,
267 Other(LanguageServerId),
268}
269
270struct LspBufferSnapshot {
271 version: i32,
272 snapshot: TextBufferSnapshot,
273}
274
275/// Message ordered with respect to buffer operations
276#[derive(Debug)]
277enum BufferOrderedMessage {
278 Operation {
279 buffer_id: BufferId,
280 operation: proto::Operation,
281 },
282 LanguageServerUpdate {
283 language_server_id: LanguageServerId,
284 message: proto::update_language_server::Variant,
285 },
286 Resync,
287}
288
289#[derive(Debug)]
290enum ProjectClientState {
291 Local,
292 Shared {
293 remote_id: u64,
294 },
295 Remote {
296 sharing_has_stopped: bool,
297 capability: Capability,
298 remote_id: u64,
299 replica_id: ReplicaId,
300 in_room: bool,
301 },
302}
303
304/// A prompt requested by LSP server.
305#[derive(Clone, Debug)]
306pub struct LanguageServerPromptRequest {
307 pub level: PromptLevel,
308 pub message: String,
309 pub actions: Vec<MessageActionItem>,
310 pub lsp_name: String,
311 response_channel: Sender<MessageActionItem>,
312}
313
314impl LanguageServerPromptRequest {
315 pub async fn respond(self, index: usize) -> Option<()> {
316 if let Some(response) = self.actions.into_iter().nth(index) {
317 self.response_channel.send(response).await.ok()
318 } else {
319 None
320 }
321 }
322}
323impl PartialEq for LanguageServerPromptRequest {
324 fn eq(&self, other: &Self) -> bool {
325 self.message == other.message && self.actions == other.actions
326 }
327}
328
329#[derive(Clone, Debug, PartialEq)]
330pub enum LanguageServerLogType {
331 Log(MessageType),
332 Trace(Option<String>),
333}
334
335#[derive(Clone, Debug, PartialEq)]
336pub enum Event {
337 LanguageServerAdded(LanguageServerId),
338 LanguageServerRemoved(LanguageServerId),
339 LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
340 Notification(String),
341 LanguageServerPrompt(LanguageServerPromptRequest),
342 LanguageNotFound(Model<Buffer>),
343 ActiveEntryChanged(Option<ProjectEntryId>),
344 ActivateProjectPanel,
345 WorktreeAdded,
346 WorktreeOrderChanged,
347 WorktreeRemoved(WorktreeId),
348 WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
349 WorktreeUpdatedGitRepositories,
350 DiskBasedDiagnosticsStarted {
351 language_server_id: LanguageServerId,
352 },
353 DiskBasedDiagnosticsFinished {
354 language_server_id: LanguageServerId,
355 },
356 DiagnosticsUpdated {
357 path: ProjectPath,
358 language_server_id: LanguageServerId,
359 },
360 RemoteIdChanged(Option<u64>),
361 DisconnectedFromHost,
362 Closed,
363 DeletedEntry(ProjectEntryId),
364 CollaboratorUpdated {
365 old_peer_id: proto::PeerId,
366 new_peer_id: proto::PeerId,
367 },
368 CollaboratorJoined(proto::PeerId),
369 CollaboratorLeft(proto::PeerId),
370 HostReshared,
371 Reshared,
372 Rejoined,
373 RefreshInlayHints,
374 RevealInProjectPanel(ProjectEntryId),
375 SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
376}
377
378pub enum LanguageServerState {
379 Starting(Task<Option<Arc<LanguageServer>>>),
380
381 Running {
382 language: Arc<Language>,
383 adapter: Arc<CachedLspAdapter>,
384 server: Arc<LanguageServer>,
385 simulate_disk_based_diagnostics_completion: Option<Task<()>>,
386 },
387}
388
389#[derive(Clone, Debug, Serialize)]
390pub struct LanguageServerStatus {
391 pub name: String,
392 pub pending_work: BTreeMap<String, LanguageServerProgress>,
393 pub has_pending_diagnostic_updates: bool,
394 progress_tokens: HashSet<String>,
395}
396
397#[derive(Clone, Debug, Serialize)]
398pub struct LanguageServerProgress {
399 pub is_disk_based_diagnostics_progress: bool,
400 pub is_cancellable: bool,
401 pub title: Option<String>,
402 pub message: Option<String>,
403 pub percentage: Option<usize>,
404 #[serde(skip_serializing)]
405 pub last_update_at: Instant,
406}
407
408#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
409pub struct ProjectPath {
410 pub worktree_id: WorktreeId,
411 pub path: Arc<Path>,
412}
413
414impl ProjectPath {
415 pub fn from_proto(p: proto::ProjectPath) -> Self {
416 Self {
417 worktree_id: WorktreeId::from_proto(p.worktree_id),
418 path: Arc::from(PathBuf::from(p.path)),
419 }
420 }
421
422 pub fn to_proto(&self) -> proto::ProjectPath {
423 proto::ProjectPath {
424 worktree_id: self.worktree_id.to_proto(),
425 path: self.path.to_string_lossy().to_string(),
426 }
427 }
428}
429
430#[derive(Debug, Clone, PartialEq, Eq)]
431pub struct InlayHint {
432 pub position: language::Anchor,
433 pub label: InlayHintLabel,
434 pub kind: Option<InlayHintKind>,
435 pub padding_left: bool,
436 pub padding_right: bool,
437 pub tooltip: Option<InlayHintTooltip>,
438 pub resolve_state: ResolveState,
439}
440
441/// The user's intent behind a given completion confirmation
442#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
443pub enum CompletionIntent {
444 /// The user intends to 'commit' this result, if possible
445 /// completion confirmations should run side effects
446 Complete,
447 /// The user intends to continue 'composing' this completion
448 /// completion confirmations should not run side effects and
449 /// let the user continue composing their action
450 Compose,
451}
452
453impl CompletionIntent {
454 pub fn is_complete(&self) -> bool {
455 self == &Self::Complete
456 }
457
458 pub fn is_compose(&self) -> bool {
459 self == &Self::Compose
460 }
461}
462
463/// A completion provided by a language server
464#[derive(Clone)]
465pub struct Completion {
466 /// The range of the buffer that will be replaced.
467 pub old_range: Range<Anchor>,
468 /// The new text that will be inserted.
469 pub new_text: String,
470 /// A label for this completion that is shown in the menu.
471 pub label: CodeLabel,
472 /// The id of the language server that produced this completion.
473 pub server_id: LanguageServerId,
474 /// The documentation for this completion.
475 pub documentation: Option<Documentation>,
476 /// The raw completion provided by the language server.
477 pub lsp_completion: lsp::CompletionItem,
478 /// An optional callback to invoke when this completion is confirmed.
479 /// Returns, whether new completions should be retriggered after the current one.
480 /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
481 /// if no confirmation is provided or `false` is returned, the completion will be committed.
482 pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut WindowContext) -> bool>>,
483}
484
485impl std::fmt::Debug for Completion {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 f.debug_struct("Completion")
488 .field("old_range", &self.old_range)
489 .field("new_text", &self.new_text)
490 .field("label", &self.label)
491 .field("server_id", &self.server_id)
492 .field("documentation", &self.documentation)
493 .field("lsp_completion", &self.lsp_completion)
494 .finish()
495 }
496}
497
498/// A completion provided by a language server
499#[derive(Clone, Debug)]
500struct CoreCompletion {
501 old_range: Range<Anchor>,
502 new_text: String,
503 server_id: LanguageServerId,
504 lsp_completion: lsp::CompletionItem,
505}
506
507/// A code action provided by a language server.
508#[derive(Clone, Debug)]
509pub struct CodeAction {
510 /// The id of the language server that produced this code action.
511 pub server_id: LanguageServerId,
512 /// The range of the buffer where this code action is applicable.
513 pub range: Range<Anchor>,
514 /// The raw code action provided by the language server.
515 pub lsp_action: lsp::CodeAction,
516}
517
518#[derive(Debug, Clone, PartialEq, Eq)]
519pub enum ResolveState {
520 Resolved,
521 CanResolve(LanguageServerId, Option<lsp::LSPAny>),
522 Resolving,
523}
524
525impl InlayHint {
526 pub fn text(&self) -> String {
527 match &self.label {
528 InlayHintLabel::String(s) => s.to_owned(),
529 InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
530 }
531 }
532}
533
534#[derive(Debug, Clone, PartialEq, Eq)]
535pub enum InlayHintLabel {
536 String(String),
537 LabelParts(Vec<InlayHintLabelPart>),
538}
539
540#[derive(Debug, Clone, PartialEq, Eq)]
541pub struct InlayHintLabelPart {
542 pub value: String,
543 pub tooltip: Option<InlayHintLabelPartTooltip>,
544 pub location: Option<(LanguageServerId, lsp::Location)>,
545}
546
547#[derive(Debug, Clone, PartialEq, Eq)]
548pub enum InlayHintTooltip {
549 String(String),
550 MarkupContent(MarkupContent),
551}
552
553#[derive(Debug, Clone, PartialEq, Eq)]
554pub enum InlayHintLabelPartTooltip {
555 String(String),
556 MarkupContent(MarkupContent),
557}
558
559#[derive(Debug, Clone, PartialEq, Eq)]
560pub struct MarkupContent {
561 pub kind: HoverBlockKind,
562 pub value: String,
563}
564
565#[derive(Debug, Clone)]
566pub struct LocationLink {
567 pub origin: Option<Location>,
568 pub target: Location,
569}
570
571#[derive(Debug)]
572pub struct DocumentHighlight {
573 pub range: Range<language::Anchor>,
574 pub kind: DocumentHighlightKind,
575}
576
577#[derive(Clone, Debug)]
578pub struct Symbol {
579 pub language_server_name: LanguageServerName,
580 pub source_worktree_id: WorktreeId,
581 pub path: ProjectPath,
582 pub label: CodeLabel,
583 pub name: String,
584 pub kind: lsp::SymbolKind,
585 pub range: Range<Unclipped<PointUtf16>>,
586 pub signature: [u8; 32],
587}
588
589#[derive(Clone, Debug)]
590struct CoreSymbol {
591 pub language_server_name: LanguageServerName,
592 pub source_worktree_id: WorktreeId,
593 pub path: ProjectPath,
594 pub name: String,
595 pub kind: lsp::SymbolKind,
596 pub range: Range<Unclipped<PointUtf16>>,
597 pub signature: [u8; 32],
598}
599
600#[derive(Clone, Debug, PartialEq)]
601pub struct HoverBlock {
602 pub text: String,
603 pub kind: HoverBlockKind,
604}
605
606#[derive(Clone, Debug, PartialEq, Eq)]
607pub enum HoverBlockKind {
608 PlainText,
609 Markdown,
610 Code { language: String },
611}
612
613#[derive(Debug, Clone)]
614pub struct Hover {
615 pub contents: Vec<HoverBlock>,
616 pub range: Option<Range<language::Anchor>>,
617 pub language: Option<Arc<Language>>,
618}
619
620impl Hover {
621 pub fn is_empty(&self) -> bool {
622 self.contents.iter().all(|block| block.text.is_empty())
623 }
624}
625
626#[derive(Default)]
627pub struct ProjectTransaction(pub HashMap<Model<Buffer>, language::Transaction>);
628
629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
630pub enum FormatTrigger {
631 Save,
632 Manual,
633}
634
635// Currently, formatting operations are represented differently depending on
636// whether they come from a language server or an external command.
637#[derive(Debug)]
638enum FormatOperation {
639 Lsp(Vec<(Range<Anchor>, String)>),
640 External(Diff),
641 Prettier(Diff),
642}
643
644impl FormatTrigger {
645 fn from_proto(value: i32) -> FormatTrigger {
646 match value {
647 0 => FormatTrigger::Save,
648 1 => FormatTrigger::Manual,
649 _ => FormatTrigger::Save,
650 }
651 }
652}
653
654#[derive(Clone)]
655pub enum DirectoryLister {
656 Project(Model<Project>),
657 Local(Arc<dyn Fs>),
658}
659
660impl DirectoryLister {
661 pub fn is_local(&self, cx: &AppContext) -> bool {
662 match self {
663 DirectoryLister::Local(_) => true,
664 DirectoryLister::Project(project) => project.read(cx).is_local_or_ssh(),
665 }
666 }
667
668 pub fn default_query(&self, cx: &mut AppContext) -> String {
669 if let DirectoryLister::Project(project) = self {
670 if let Some(worktree) = project.read(cx).visible_worktrees(cx).next() {
671 return worktree.read(cx).abs_path().to_string_lossy().to_string();
672 }
673 };
674 "~/".to_string()
675 }
676
677 pub fn list_directory(&self, path: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
678 match self {
679 DirectoryLister::Project(project) => {
680 project.update(cx, |project, cx| project.list_directory(path, cx))
681 }
682 DirectoryLister::Local(fs) => {
683 let fs = fs.clone();
684 cx.background_executor().spawn(async move {
685 let mut results = vec![];
686 let expanded = shellexpand::tilde(&path);
687 let query = Path::new(expanded.as_ref());
688 let mut response = fs.read_dir(query).await?;
689 while let Some(path) = response.next().await {
690 if let Some(file_name) = path?.file_name() {
691 results.push(PathBuf::from(file_name.to_os_string()));
692 }
693 }
694 Ok(results)
695 })
696 }
697 }
698 }
699}
700
701#[cfg(any(test, feature = "test-support"))]
702pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
703 trigger_kind: lsp::CompletionTriggerKind::INVOKED,
704 trigger_character: None,
705};
706
707impl Project {
708 pub fn init_settings(cx: &mut AppContext) {
709 WorktreeSettings::register(cx);
710 ProjectSettings::register(cx);
711 }
712
713 pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
714 connection_manager::init(client.clone(), cx);
715 Self::init_settings(cx);
716
717 client.add_model_message_handler(Self::handle_add_collaborator);
718 client.add_model_message_handler(Self::handle_update_project_collaborator);
719 client.add_model_message_handler(Self::handle_remove_collaborator);
720 client.add_model_message_handler(Self::handle_start_language_server);
721 client.add_model_message_handler(Self::handle_update_language_server);
722 client.add_model_message_handler(Self::handle_update_project);
723 client.add_model_message_handler(Self::handle_unshare_project);
724 client.add_model_message_handler(Self::handle_create_buffer_for_peer);
725 client.add_model_request_handler(Self::handle_update_buffer);
726 client.add_model_message_handler(Self::handle_update_diagnostic_summary);
727 client.add_model_message_handler(Self::handle_update_worktree);
728 client.add_model_message_handler(Self::handle_update_worktree_settings);
729 client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
730 client.add_model_request_handler(Self::handle_resolve_completion_documentation);
731 client.add_model_request_handler(Self::handle_apply_code_action);
732 client.add_model_request_handler(Self::handle_on_type_formatting);
733 client.add_model_request_handler(Self::handle_inlay_hints);
734 client.add_model_request_handler(Self::handle_resolve_inlay_hint);
735 client.add_model_request_handler(Self::handle_refresh_inlay_hints);
736 client.add_model_request_handler(Self::handle_reload_buffers);
737 client.add_model_request_handler(Self::handle_synchronize_buffers);
738 client.add_model_request_handler(Self::handle_format_buffers);
739 client.add_model_request_handler(Self::handle_lsp_command::<GetCodeActions>);
740 client.add_model_request_handler(Self::handle_lsp_command::<GetCompletions>);
741 client.add_model_request_handler(Self::handle_lsp_command::<GetHover>);
742 client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
743 client.add_model_request_handler(Self::handle_lsp_command::<GetDeclaration>);
744 client.add_model_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
745 client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
746 client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
747 client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
748 client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
749 client.add_model_request_handler(Self::handle_search_project);
750 client.add_model_request_handler(Self::handle_search_candidate_buffers);
751 client.add_model_request_handler(Self::handle_get_project_symbols);
752 client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
753 client.add_model_request_handler(Self::handle_open_buffer_by_id);
754 client.add_model_request_handler(Self::handle_open_buffer_by_path);
755 client.add_model_request_handler(Self::handle_open_new_buffer);
756 client.add_model_request_handler(Self::handle_lsp_command::<lsp_ext_command::ExpandMacro>);
757 client.add_model_request_handler(Self::handle_multi_lsp_query);
758 client.add_model_request_handler(Self::handle_restart_language_servers);
759 client.add_model_request_handler(Self::handle_task_context_for_location);
760 client.add_model_request_handler(Self::handle_task_templates);
761 client.add_model_request_handler(Self::handle_lsp_command::<LinkedEditingRange>);
762
763 client.add_model_request_handler(WorktreeStore::handle_create_project_entry);
764 client.add_model_request_handler(WorktreeStore::handle_rename_project_entry);
765 client.add_model_request_handler(WorktreeStore::handle_copy_project_entry);
766 client.add_model_request_handler(WorktreeStore::handle_delete_project_entry);
767 client.add_model_request_handler(WorktreeStore::handle_expand_project_entry);
768
769 client.add_model_message_handler(BufferStore::handle_buffer_reloaded);
770 client.add_model_message_handler(BufferStore::handle_buffer_saved);
771 client.add_model_message_handler(BufferStore::handle_update_buffer_file);
772 client.add_model_message_handler(BufferStore::handle_update_diff_base);
773 client.add_model_request_handler(BufferStore::handle_save_buffer);
774 client.add_model_request_handler(BufferStore::handle_blame_buffer);
775 }
776
777 pub fn local(
778 client: Arc<Client>,
779 node: Arc<dyn NodeRuntime>,
780 user_store: Model<UserStore>,
781 languages: Arc<LanguageRegistry>,
782 fs: Arc<dyn Fs>,
783 cx: &mut AppContext,
784 ) -> Model<Self> {
785 cx.new_model(|cx: &mut ModelContext<Self>| {
786 let (tx, rx) = mpsc::unbounded();
787 cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
788 .detach();
789 let tasks = Inventory::new(cx);
790 let global_snippets_dir = paths::config_dir().join("snippets");
791 let snippets =
792 SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
793
794 let worktree_store = cx.new_model(|_| WorktreeStore::new(false));
795 cx.subscribe(&worktree_store, Self::on_worktree_store_event)
796 .detach();
797
798 let buffer_store =
799 cx.new_model(|cx| BufferStore::new(worktree_store.clone(), None, cx));
800 cx.subscribe(&buffer_store, Self::on_buffer_store_event)
801 .detach();
802
803 let yarn = YarnPathStore::new(fs.clone(), cx);
804
805 Self {
806 buffer_ordered_messages_tx: tx,
807 collaborators: Default::default(),
808 worktree_store,
809 buffer_store,
810 shared_buffers: Default::default(),
811 loading_worktrees: Default::default(),
812 buffer_snapshots: Default::default(),
813 join_project_response_message_id: 0,
814 client_state: ProjectClientState::Local,
815 client_subscriptions: Vec::new(),
816 _subscriptions: vec![
817 cx.observe_global::<SettingsStore>(Self::on_settings_changed),
818 cx.on_release(Self::release),
819 cx.on_app_quit(Self::shutdown_language_servers),
820 ],
821 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
822 _maintain_workspace_config: Self::maintain_workspace_config(cx),
823 active_entry: None,
824 yarn,
825 snippets,
826 languages,
827 client,
828 user_store,
829 fs,
830 ssh_session: None,
831 next_entry_id: Default::default(),
832 next_diagnostic_group_id: Default::default(),
833 diagnostics: Default::default(),
834 diagnostic_summaries: Default::default(),
835 supplementary_language_servers: HashMap::default(),
836 language_servers: Default::default(),
837 language_server_ids: HashMap::default(),
838 language_server_statuses: Default::default(),
839 last_formatting_failure: None,
840 last_workspace_edits_by_language_server: Default::default(),
841 language_server_watched_paths: HashMap::default(),
842 language_server_watcher_registrations: HashMap::default(),
843 buffers_being_formatted: Default::default(),
844 buffers_needing_diff: Default::default(),
845 git_diff_debouncer: DebouncedDelay::new(),
846 nonce: StdRng::from_entropy().gen(),
847 terminals: Terminals {
848 local_handles: Vec::new(),
849 },
850 current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
851 node: Some(node),
852 default_prettier: DefaultPrettier::default(),
853 prettiers_per_worktree: HashMap::default(),
854 prettier_instances: HashMap::default(),
855 tasks,
856 hosted_project_id: None,
857 dev_server_project_id: None,
858 search_history: Self::new_search_history(),
859 cached_shell_environments: HashMap::default(),
860 remotely_created_buffers: Default::default(),
861 }
862 })
863 }
864
865 pub fn ssh(
866 ssh: Arc<SshSession>,
867 client: Arc<Client>,
868 node: Arc<dyn NodeRuntime>,
869 user_store: Model<UserStore>,
870 languages: Arc<LanguageRegistry>,
871 fs: Arc<dyn Fs>,
872 cx: &mut AppContext,
873 ) -> Model<Self> {
874 let this = Self::local(client, node, user_store, languages, fs, cx);
875 this.update(cx, |this, cx| {
876 let buffer_store = this.buffer_store.downgrade();
877
878 ssh.add_message_handler(cx.weak_model(), Self::handle_update_worktree);
879 ssh.add_message_handler(cx.weak_model(), Self::handle_create_buffer_for_peer);
880 ssh.add_message_handler(buffer_store.clone(), BufferStore::handle_update_buffer_file);
881 ssh.add_message_handler(buffer_store.clone(), BufferStore::handle_update_diff_base);
882
883 this.ssh_session = Some(ssh);
884 });
885 this
886 }
887
888 pub async fn remote(
889 remote_id: u64,
890 client: Arc<Client>,
891 user_store: Model<UserStore>,
892 languages: Arc<LanguageRegistry>,
893 fs: Arc<dyn Fs>,
894 cx: AsyncAppContext,
895 ) -> Result<Model<Self>> {
896 let project =
897 Self::in_room(remote_id, client, user_store, languages, fs, cx.clone()).await?;
898 cx.update(|cx| {
899 connection_manager::Manager::global(cx).update(cx, |manager, cx| {
900 manager.maintain_project_connection(&project, cx)
901 })
902 })?;
903 Ok(project)
904 }
905
906 pub async fn in_room(
907 remote_id: u64,
908 client: Arc<Client>,
909 user_store: Model<UserStore>,
910 languages: Arc<LanguageRegistry>,
911 fs: Arc<dyn Fs>,
912 cx: AsyncAppContext,
913 ) -> Result<Model<Self>> {
914 client.authenticate_and_connect(true, &cx).await?;
915
916 let subscriptions = (
917 client.subscribe_to_entity::<Self>(remote_id)?,
918 client.subscribe_to_entity::<BufferStore>(remote_id)?,
919 );
920 let response = client
921 .request_envelope(proto::JoinProject {
922 project_id: remote_id,
923 })
924 .await?;
925 Self::from_join_project_response(
926 response,
927 subscriptions,
928 client,
929 user_store,
930 languages,
931 fs,
932 cx,
933 )
934 .await
935 }
936
937 async fn from_join_project_response(
938 response: TypedEnvelope<proto::JoinProjectResponse>,
939 subscription: (
940 PendingEntitySubscription<Project>,
941 PendingEntitySubscription<BufferStore>,
942 ),
943 client: Arc<Client>,
944 user_store: Model<UserStore>,
945 languages: Arc<LanguageRegistry>,
946 fs: Arc<dyn Fs>,
947 mut cx: AsyncAppContext,
948 ) -> Result<Model<Self>> {
949 let remote_id = response.payload.project_id;
950 let role = response.payload.role();
951
952 let worktree_store = cx.new_model(|_| WorktreeStore::new(true))?;
953 let buffer_store =
954 cx.new_model(|cx| BufferStore::new(worktree_store.clone(), Some(remote_id), cx))?;
955
956 let this = cx.new_model(|cx| {
957 let replica_id = response.payload.replica_id as ReplicaId;
958 let tasks = Inventory::new(cx);
959 let global_snippets_dir = paths::config_dir().join("snippets");
960 let snippets =
961 SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
962 let yarn = YarnPathStore::new(fs.clone(), cx);
963
964 let mut worktrees = Vec::new();
965 for worktree in response.payload.worktrees {
966 let worktree =
967 Worktree::remote(remote_id, replica_id, worktree, client.clone().into(), cx);
968 worktrees.push(worktree);
969 }
970
971 let (tx, rx) = mpsc::unbounded();
972 cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
973 .detach();
974
975 cx.subscribe(&buffer_store, Self::on_buffer_store_event)
976 .detach();
977
978 let mut this = Self {
979 buffer_ordered_messages_tx: tx,
980 buffer_store: buffer_store.clone(),
981 worktree_store,
982 shared_buffers: Default::default(),
983 loading_worktrees: Default::default(),
984 active_entry: None,
985 collaborators: Default::default(),
986 join_project_response_message_id: response.message_id,
987 _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
988 _maintain_workspace_config: Self::maintain_workspace_config(cx),
989 languages,
990 user_store: user_store.clone(),
991 snippets,
992 yarn,
993 fs,
994 ssh_session: None,
995 next_entry_id: Default::default(),
996 next_diagnostic_group_id: Default::default(),
997 diagnostic_summaries: Default::default(),
998 diagnostics: Default::default(),
999 client_subscriptions: Default::default(),
1000 _subscriptions: vec![
1001 cx.on_release(Self::release),
1002 cx.on_app_quit(Self::shutdown_language_servers),
1003 ],
1004 client: client.clone(),
1005 client_state: ProjectClientState::Remote {
1006 sharing_has_stopped: false,
1007 capability: Capability::ReadWrite,
1008 remote_id,
1009 replica_id,
1010 in_room: response.payload.dev_server_project_id.is_none(),
1011 },
1012 supplementary_language_servers: HashMap::default(),
1013 language_servers: Default::default(),
1014 language_server_ids: HashMap::default(),
1015 language_server_statuses: response
1016 .payload
1017 .language_servers
1018 .into_iter()
1019 .map(|server| {
1020 (
1021 LanguageServerId(server.id as usize),
1022 LanguageServerStatus {
1023 name: server.name,
1024 pending_work: Default::default(),
1025 has_pending_diagnostic_updates: false,
1026 progress_tokens: Default::default(),
1027 },
1028 )
1029 })
1030 .collect(),
1031 last_formatting_failure: None,
1032 last_workspace_edits_by_language_server: Default::default(),
1033 language_server_watched_paths: HashMap::default(),
1034 language_server_watcher_registrations: HashMap::default(),
1035 buffers_being_formatted: Default::default(),
1036 buffers_needing_diff: Default::default(),
1037 git_diff_debouncer: DebouncedDelay::new(),
1038 buffer_snapshots: Default::default(),
1039 nonce: StdRng::from_entropy().gen(),
1040 terminals: Terminals {
1041 local_handles: Vec::new(),
1042 },
1043 current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
1044 node: None,
1045 default_prettier: DefaultPrettier::default(),
1046 prettiers_per_worktree: HashMap::default(),
1047 prettier_instances: HashMap::default(),
1048 tasks,
1049 hosted_project_id: None,
1050 dev_server_project_id: response
1051 .payload
1052 .dev_server_project_id
1053 .map(|dev_server_project_id| DevServerProjectId(dev_server_project_id)),
1054 search_history: Self::new_search_history(),
1055 cached_shell_environments: HashMap::default(),
1056 remotely_created_buffers: Arc::new(Mutex::new(RemotelyCreatedBuffers::default())),
1057 };
1058 this.set_role(role, cx);
1059 for worktree in worktrees {
1060 let _ = this.add_worktree(&worktree, cx);
1061 }
1062 this
1063 })?;
1064
1065 let subscriptions = [
1066 subscription.0.set_model(&this, &mut cx),
1067 subscription.1.set_model(&buffer_store, &mut cx),
1068 ];
1069
1070 let user_ids = response
1071 .payload
1072 .collaborators
1073 .iter()
1074 .map(|peer| peer.user_id)
1075 .collect();
1076 user_store
1077 .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
1078 .await?;
1079
1080 this.update(&mut cx, |this, cx| {
1081 this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
1082 this.client_subscriptions.extend(subscriptions);
1083 anyhow::Ok(())
1084 })??;
1085
1086 Ok(this)
1087 }
1088
1089 pub async fn hosted(
1090 remote_id: ProjectId,
1091 user_store: Model<UserStore>,
1092 client: Arc<Client>,
1093 languages: Arc<LanguageRegistry>,
1094 fs: Arc<dyn Fs>,
1095 cx: AsyncAppContext,
1096 ) -> Result<Model<Self>> {
1097 client.authenticate_and_connect(true, &cx).await?;
1098
1099 let subscriptions = (
1100 client.subscribe_to_entity::<Self>(remote_id.0)?,
1101 client.subscribe_to_entity::<BufferStore>(remote_id.0)?,
1102 );
1103 let response = client
1104 .request_envelope(proto::JoinHostedProject {
1105 project_id: remote_id.0,
1106 })
1107 .await?;
1108 Self::from_join_project_response(
1109 response,
1110 subscriptions,
1111 client,
1112 user_store,
1113 languages,
1114 fs,
1115 cx,
1116 )
1117 .await
1118 }
1119
1120 fn new_search_history() -> SearchHistory {
1121 SearchHistory::new(
1122 Some(MAX_PROJECT_SEARCH_HISTORY_SIZE),
1123 search_history::QueryInsertionBehavior::AlwaysInsert,
1124 )
1125 }
1126
1127 fn release(&mut self, cx: &mut AppContext) {
1128 match &self.client_state {
1129 ProjectClientState::Local => {}
1130 ProjectClientState::Shared { .. } => {
1131 let _ = self.unshare_internal(cx);
1132 }
1133 ProjectClientState::Remote { remote_id, .. } => {
1134 let _ = self.client.send(proto::LeaveProject {
1135 project_id: *remote_id,
1136 });
1137 self.disconnected_from_host_internal(cx);
1138 }
1139 }
1140 }
1141
1142 fn shutdown_language_servers(
1143 &mut self,
1144 _cx: &mut ModelContext<Self>,
1145 ) -> impl Future<Output = ()> {
1146 let shutdown_futures = self
1147 .language_servers
1148 .drain()
1149 .map(|(_, server_state)| async {
1150 use LanguageServerState::*;
1151 match server_state {
1152 Running { server, .. } => server.shutdown()?.await,
1153 Starting(task) => task.await?.shutdown()?.await,
1154 }
1155 })
1156 .collect::<Vec<_>>();
1157
1158 async move {
1159 futures::future::join_all(shutdown_futures).await;
1160 }
1161 }
1162
1163 #[cfg(any(test, feature = "test-support"))]
1164 pub async fn example(
1165 root_paths: impl IntoIterator<Item = &Path>,
1166 cx: &mut AsyncAppContext,
1167 ) -> Model<Project> {
1168 use clock::FakeSystemClock;
1169
1170 let fs = Arc::new(RealFs::default());
1171 let languages = LanguageRegistry::test(cx.background_executor().clone());
1172 let clock = Arc::new(FakeSystemClock::default());
1173 let http_client = http_client::FakeHttpClient::with_404_response();
1174 let client = cx
1175 .update(|cx| client::Client::new(clock, http_client.clone(), cx))
1176 .unwrap();
1177 let user_store = cx
1178 .new_model(|cx| UserStore::new(client.clone(), cx))
1179 .unwrap();
1180 let project = cx
1181 .update(|cx| {
1182 Project::local(
1183 client,
1184 node_runtime::FakeNodeRuntime::new(),
1185 user_store,
1186 Arc::new(languages),
1187 fs,
1188 cx,
1189 )
1190 })
1191 .unwrap();
1192 for path in root_paths {
1193 let (tree, _) = project
1194 .update(cx, |project, cx| {
1195 project.find_or_create_worktree(path, true, cx)
1196 })
1197 .unwrap()
1198 .await
1199 .unwrap();
1200 tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1201 .unwrap()
1202 .await;
1203 }
1204 project
1205 }
1206
1207 #[cfg(any(test, feature = "test-support"))]
1208 pub async fn test(
1209 fs: Arc<dyn Fs>,
1210 root_paths: impl IntoIterator<Item = &Path>,
1211 cx: &mut gpui::TestAppContext,
1212 ) -> Model<Project> {
1213 use clock::FakeSystemClock;
1214
1215 let languages = LanguageRegistry::test(cx.executor());
1216 let clock = Arc::new(FakeSystemClock::default());
1217 let http_client = http_client::FakeHttpClient::with_404_response();
1218 let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
1219 let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
1220 let project = cx.update(|cx| {
1221 Project::local(
1222 client,
1223 node_runtime::FakeNodeRuntime::new(),
1224 user_store,
1225 Arc::new(languages),
1226 fs,
1227 cx,
1228 )
1229 });
1230 for path in root_paths {
1231 let (tree, _) = project
1232 .update(cx, |project, cx| {
1233 project.find_or_create_worktree(path, true, cx)
1234 })
1235 .await
1236 .unwrap();
1237
1238 project.update(cx, |project, cx| {
1239 let tree_id = tree.read(cx).id();
1240 // In tests we always populate the environment to be empty so we don't run the shell
1241 project
1242 .cached_shell_environments
1243 .insert(tree_id, HashMap::default());
1244 });
1245
1246 tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1247 .await;
1248 }
1249 project
1250 }
1251
1252 fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
1253 let mut language_servers_to_start = Vec::new();
1254 let mut language_formatters_to_check = Vec::new();
1255 for buffer in self.buffer_store.read(cx).buffers() {
1256 let buffer = buffer.read(cx);
1257 let buffer_file = File::from_dyn(buffer.file());
1258 let buffer_language = buffer.language();
1259 let settings = language_settings(buffer_language, buffer.file(), cx);
1260 if let Some(language) = buffer_language {
1261 if settings.enable_language_server {
1262 if let Some(file) = buffer_file {
1263 language_servers_to_start
1264 .push((file.worktree.clone(), Arc::clone(language)));
1265 }
1266 }
1267 language_formatters_to_check
1268 .push((buffer_file.map(|f| f.worktree_id(cx)), settings.clone()));
1269 }
1270 }
1271
1272 let mut language_servers_to_stop = Vec::new();
1273 let mut language_servers_to_restart = Vec::new();
1274 let languages = self.languages.to_vec();
1275
1276 let new_lsp_settings = ProjectSettings::get_global(cx).lsp.clone();
1277 let current_lsp_settings = &self.current_lsp_settings;
1278 for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
1279 let language = languages.iter().find_map(|l| {
1280 let adapter = self
1281 .languages
1282 .lsp_adapters(l)
1283 .iter()
1284 .find(|adapter| &adapter.name == started_lsp_name)?
1285 .clone();
1286 Some((l, adapter))
1287 });
1288 if let Some((language, adapter)) = language {
1289 let worktree = self.worktree_for_id(*worktree_id, cx);
1290 let file = worktree.as_ref().and_then(|tree| {
1291 tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _))
1292 });
1293 if !language_settings(Some(language), file.as_ref(), cx).enable_language_server {
1294 language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
1295 } else if let Some(worktree) = worktree {
1296 let server_name = &adapter.name.0;
1297 match (
1298 current_lsp_settings.get(server_name),
1299 new_lsp_settings.get(server_name),
1300 ) {
1301 (None, None) => {}
1302 (Some(_), None) | (None, Some(_)) => {
1303 language_servers_to_restart.push((worktree, Arc::clone(language)));
1304 }
1305 (Some(current_lsp_settings), Some(new_lsp_settings)) => {
1306 if current_lsp_settings != new_lsp_settings {
1307 language_servers_to_restart.push((worktree, Arc::clone(language)));
1308 }
1309 }
1310 }
1311 }
1312 }
1313 }
1314 self.current_lsp_settings = new_lsp_settings;
1315
1316 // Stop all newly-disabled language servers.
1317 for (worktree_id, adapter_name) in language_servers_to_stop {
1318 self.stop_language_server(worktree_id, adapter_name, cx)
1319 .detach();
1320 }
1321
1322 let mut prettier_plugins_by_worktree = HashMap::default();
1323 for (worktree, language_settings) in language_formatters_to_check {
1324 if let Some(plugins) =
1325 prettier_support::prettier_plugins_for_language(&language_settings)
1326 {
1327 prettier_plugins_by_worktree
1328 .entry(worktree)
1329 .or_insert_with(|| HashSet::default())
1330 .extend(plugins.iter().cloned());
1331 }
1332 }
1333 for (worktree, prettier_plugins) in prettier_plugins_by_worktree {
1334 self.install_default_prettier(
1335 worktree,
1336 prettier_plugins.into_iter().map(Arc::from),
1337 cx,
1338 );
1339 }
1340
1341 // Start all the newly-enabled language servers.
1342 for (worktree, language) in language_servers_to_start {
1343 self.start_language_servers(&worktree, language, cx);
1344 }
1345
1346 // Restart all language servers with changed initialization options.
1347 for (worktree, language) in language_servers_to_restart {
1348 self.restart_language_servers(worktree, language, cx);
1349 }
1350
1351 cx.notify();
1352 }
1353
1354 pub fn buffer_for_id(&self, remote_id: BufferId, cx: &AppContext) -> Option<Model<Buffer>> {
1355 self.buffer_store.read(cx).get(remote_id)
1356 }
1357
1358 pub fn languages(&self) -> &Arc<LanguageRegistry> {
1359 &self.languages
1360 }
1361
1362 pub fn client(&self) -> Arc<Client> {
1363 self.client.clone()
1364 }
1365
1366 pub fn user_store(&self) -> Model<UserStore> {
1367 self.user_store.clone()
1368 }
1369
1370 pub fn node_runtime(&self) -> Option<&Arc<dyn NodeRuntime>> {
1371 self.node.as_ref()
1372 }
1373
1374 pub fn opened_buffers(&self, cx: &AppContext) -> Vec<Model<Buffer>> {
1375 self.buffer_store.read(cx).buffers().collect()
1376 }
1377
1378 #[cfg(any(test, feature = "test-support"))]
1379 pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
1380 self.buffer_store
1381 .read(cx)
1382 .get_by_path(&path.into(), cx)
1383 .is_some()
1384 }
1385
1386 pub fn fs(&self) -> &Arc<dyn Fs> {
1387 &self.fs
1388 }
1389
1390 pub fn remote_id(&self) -> Option<u64> {
1391 match self.client_state {
1392 ProjectClientState::Local => None,
1393 ProjectClientState::Shared { remote_id, .. }
1394 | ProjectClientState::Remote { remote_id, .. } => Some(remote_id),
1395 }
1396 }
1397
1398 pub fn hosted_project_id(&self) -> Option<ProjectId> {
1399 self.hosted_project_id
1400 }
1401
1402 pub fn dev_server_project_id(&self) -> Option<DevServerProjectId> {
1403 self.dev_server_project_id
1404 }
1405
1406 pub fn supports_remote_terminal(&self, cx: &AppContext) -> bool {
1407 let Some(id) = self.dev_server_project_id else {
1408 return false;
1409 };
1410 let Some(server) = dev_server_projects::Store::global(cx)
1411 .read(cx)
1412 .dev_server_for_project(id)
1413 else {
1414 return false;
1415 };
1416 server.ssh_connection_string.is_some()
1417 }
1418
1419 pub fn ssh_connection_string(&self, cx: &ModelContext<Self>) -> Option<SharedString> {
1420 if self.is_local_or_ssh() {
1421 return None;
1422 }
1423
1424 let dev_server_id = self.dev_server_project_id()?;
1425 dev_server_projects::Store::global(cx)
1426 .read(cx)
1427 .dev_server_for_project(dev_server_id)?
1428 .ssh_connection_string
1429 .clone()
1430 }
1431
1432 pub fn replica_id(&self) -> ReplicaId {
1433 match self.client_state {
1434 ProjectClientState::Remote { replica_id, .. } => replica_id,
1435 _ => 0,
1436 }
1437 }
1438
1439 fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
1440 cx.notify();
1441 let ProjectClientState::Shared { remote_id } = self.client_state else {
1442 return;
1443 };
1444 let worktrees = self.worktrees(cx).collect::<Vec<_>>();
1445 let project_id = remote_id;
1446
1447 let update_project = self.client.request(proto::UpdateProject {
1448 project_id,
1449 worktrees: self.worktree_metadata_protos(cx),
1450 });
1451 cx.spawn(|this, mut cx| async move {
1452 update_project.await?;
1453
1454 this.update(&mut cx, |this, cx| {
1455 let client = this.client.clone();
1456 for worktree in worktrees {
1457 worktree.update(cx, |worktree, cx| {
1458 if let Some(summaries) = this.diagnostic_summaries.get(&worktree.id()) {
1459 for (path, summaries) in summaries {
1460 for (&server_id, summary) in summaries {
1461 this.client.send(proto::UpdateDiagnosticSummary {
1462 project_id,
1463 worktree_id: worktree.id().to_proto(),
1464 summary: Some(summary.to_proto(server_id, path)),
1465 })?;
1466 }
1467 }
1468 }
1469
1470 worktree.observe_updates(project_id, cx, {
1471 let client = client.clone();
1472 move |update| client.request(update).map(|result| result.is_ok())
1473 });
1474
1475 anyhow::Ok(())
1476 })?;
1477 }
1478 anyhow::Ok(())
1479 })
1480 })
1481 .detach_and_log_err(cx);
1482 }
1483
1484 pub fn task_inventory(&self) -> &Model<Inventory> {
1485 &self.tasks
1486 }
1487
1488 pub fn snippets(&self) -> &Model<SnippetProvider> {
1489 &self.snippets
1490 }
1491
1492 pub fn search_history(&self) -> &SearchHistory {
1493 &self.search_history
1494 }
1495
1496 pub fn search_history_mut(&mut self) -> &mut SearchHistory {
1497 &mut self.search_history
1498 }
1499
1500 pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
1501 &self.collaborators
1502 }
1503
1504 pub fn host(&self) -> Option<&Collaborator> {
1505 self.collaborators.values().find(|c| c.replica_id == 0)
1506 }
1507
1508 pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut AppContext) {
1509 self.worktree_store.update(cx, |store, _| {
1510 store.set_worktrees_reordered(worktrees_reordered);
1511 });
1512 }
1513
1514 /// Collect all worktrees, including ones that don't appear in the project panel
1515 pub fn worktrees<'a>(
1516 &self,
1517 cx: &'a AppContext,
1518 ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1519 self.worktree_store.read(cx).worktrees()
1520 }
1521
1522 /// Collect all user-visible worktrees, the ones that appear in the project panel.
1523 pub fn visible_worktrees<'a>(
1524 &'a self,
1525 cx: &'a AppContext,
1526 ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1527 self.worktree_store.read(cx).visible_worktrees(cx)
1528 }
1529
1530 pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
1531 self.visible_worktrees(cx)
1532 .map(|tree| tree.read(cx).root_name())
1533 }
1534
1535 pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
1536 self.worktree_store.read(cx).worktree_for_id(id, cx)
1537 }
1538
1539 pub fn worktree_for_entry(
1540 &self,
1541 entry_id: ProjectEntryId,
1542 cx: &AppContext,
1543 ) -> Option<Model<Worktree>> {
1544 self.worktree_store
1545 .read(cx)
1546 .worktree_for_entry(entry_id, cx)
1547 }
1548
1549 pub fn worktree_id_for_entry(
1550 &self,
1551 entry_id: ProjectEntryId,
1552 cx: &AppContext,
1553 ) -> Option<WorktreeId> {
1554 self.worktree_for_entry(entry_id, cx)
1555 .map(|worktree| worktree.read(cx).id())
1556 }
1557
1558 /// Checks if the entry is the root of a worktree.
1559 pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &AppContext) -> bool {
1560 self.worktree_for_entry(entry_id, cx)
1561 .map(|worktree| {
1562 worktree
1563 .read(cx)
1564 .root_entry()
1565 .is_some_and(|e| e.id == entry_id)
1566 })
1567 .unwrap_or(false)
1568 }
1569
1570 pub fn visibility_for_paths(&self, paths: &[PathBuf], cx: &AppContext) -> Option<bool> {
1571 paths
1572 .iter()
1573 .map(|path| self.visibility_for_path(path, cx))
1574 .max()
1575 .flatten()
1576 }
1577
1578 pub fn visibility_for_path(&self, path: &Path, cx: &AppContext) -> Option<bool> {
1579 self.worktrees(cx)
1580 .filter_map(|worktree| {
1581 let worktree = worktree.read(cx);
1582 worktree
1583 .as_local()?
1584 .contains_abs_path(path)
1585 .then(|| worktree.is_visible())
1586 })
1587 .max()
1588 }
1589
1590 pub fn create_entry(
1591 &mut self,
1592 project_path: impl Into<ProjectPath>,
1593 is_directory: bool,
1594 cx: &mut ModelContext<Self>,
1595 ) -> Task<Result<CreatedEntry>> {
1596 let project_path = project_path.into();
1597 let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
1598 return Task::ready(Err(anyhow!(format!(
1599 "No worktree for path {project_path:?}"
1600 ))));
1601 };
1602 worktree.update(cx, |worktree, cx| {
1603 worktree.create_entry(project_path.path, is_directory, cx)
1604 })
1605 }
1606
1607 pub fn copy_entry(
1608 &mut self,
1609 entry_id: ProjectEntryId,
1610 new_path: impl Into<Arc<Path>>,
1611 cx: &mut ModelContext<Self>,
1612 ) -> Task<Result<Option<Entry>>> {
1613 let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1614 return Task::ready(Ok(None));
1615 };
1616 worktree.update(cx, |worktree, cx| {
1617 worktree.copy_entry(entry_id, new_path, cx)
1618 })
1619 }
1620
1621 pub fn rename_entry(
1622 &mut self,
1623 entry_id: ProjectEntryId,
1624 new_path: impl Into<Arc<Path>>,
1625 cx: &mut ModelContext<Self>,
1626 ) -> Task<Result<CreatedEntry>> {
1627 let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1628 return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
1629 };
1630 worktree.update(cx, |worktree, cx| {
1631 worktree.rename_entry(entry_id, new_path, cx)
1632 })
1633 }
1634
1635 pub fn delete_entry(
1636 &mut self,
1637 entry_id: ProjectEntryId,
1638 trash: bool,
1639 cx: &mut ModelContext<Self>,
1640 ) -> Option<Task<Result<()>>> {
1641 let worktree = self.worktree_for_entry(entry_id, cx)?;
1642 worktree.update(cx, |worktree, cx| {
1643 worktree.delete_entry(entry_id, trash, cx)
1644 })
1645 }
1646
1647 pub fn expand_entry(
1648 &mut self,
1649 worktree_id: WorktreeId,
1650 entry_id: ProjectEntryId,
1651 cx: &mut ModelContext<Self>,
1652 ) -> Option<Task<Result<()>>> {
1653 let worktree = self.worktree_for_id(worktree_id, cx)?;
1654 worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
1655 }
1656
1657 pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
1658 if !matches!(self.client_state, ProjectClientState::Local) {
1659 if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1660 if *in_room || self.dev_server_project_id.is_none() {
1661 return Err(anyhow!("project was already shared"));
1662 } else {
1663 *in_room = true;
1664 return Ok(());
1665 }
1666 } else {
1667 return Err(anyhow!("project was already shared"));
1668 }
1669 }
1670 self.client_subscriptions.extend([
1671 self.client
1672 .subscribe_to_entity(project_id)?
1673 .set_model(&cx.handle(), &mut cx.to_async()),
1674 self.client
1675 .subscribe_to_entity(project_id)?
1676 .set_model(&self.worktree_store, &mut cx.to_async()),
1677 self.client
1678 .subscribe_to_entity(project_id)?
1679 .set_model(&self.buffer_store, &mut cx.to_async()),
1680 ]);
1681
1682 self.buffer_store.update(cx, |buffer_store, cx| {
1683 buffer_store.set_remote_id(Some(project_id), cx)
1684 });
1685 self.worktree_store.update(cx, |store, cx| {
1686 store.set_shared(true, cx);
1687 });
1688
1689 for (server_id, status) in &self.language_server_statuses {
1690 self.client
1691 .send(proto::StartLanguageServer {
1692 project_id,
1693 server: Some(proto::LanguageServer {
1694 id: server_id.0 as u64,
1695 name: status.name.clone(),
1696 }),
1697 })
1698 .log_err();
1699 }
1700
1701 let store = cx.global::<SettingsStore>();
1702 for worktree in self.worktrees(cx) {
1703 let worktree_id = worktree.read(cx).id().to_proto();
1704 for (path, content) in store.local_settings(worktree.entity_id().as_u64() as usize) {
1705 self.client
1706 .send(proto::UpdateWorktreeSettings {
1707 project_id,
1708 worktree_id,
1709 path: path.to_string_lossy().into(),
1710 content: Some(content),
1711 })
1712 .log_err();
1713 }
1714 }
1715
1716 self.client_state = ProjectClientState::Shared {
1717 remote_id: project_id,
1718 };
1719
1720 self.metadata_changed(cx);
1721 cx.emit(Event::RemoteIdChanged(Some(project_id)));
1722 cx.notify();
1723 Ok(())
1724 }
1725
1726 pub fn reshared(
1727 &mut self,
1728 message: proto::ResharedProject,
1729 cx: &mut ModelContext<Self>,
1730 ) -> Result<()> {
1731 self.shared_buffers.clear();
1732 self.set_collaborators_from_proto(message.collaborators, cx)?;
1733 self.metadata_changed(cx);
1734 cx.emit(Event::Reshared);
1735 Ok(())
1736 }
1737
1738 pub fn rejoined(
1739 &mut self,
1740 message: proto::RejoinedProject,
1741 message_id: u32,
1742 cx: &mut ModelContext<Self>,
1743 ) -> Result<()> {
1744 cx.update_global::<SettingsStore, _>(|store, cx| {
1745 self.worktree_store.update(cx, |worktree_store, cx| {
1746 for worktree in worktree_store.worktrees() {
1747 store
1748 .clear_local_settings(worktree.entity_id().as_u64() as usize, cx)
1749 .log_err();
1750 }
1751 });
1752 });
1753
1754 self.join_project_response_message_id = message_id;
1755 self.set_worktrees_from_proto(message.worktrees, cx)?;
1756 self.set_collaborators_from_proto(message.collaborators, cx)?;
1757 self.language_server_statuses = message
1758 .language_servers
1759 .into_iter()
1760 .map(|server| {
1761 (
1762 LanguageServerId(server.id as usize),
1763 LanguageServerStatus {
1764 name: server.name,
1765 pending_work: Default::default(),
1766 has_pending_diagnostic_updates: false,
1767 progress_tokens: Default::default(),
1768 },
1769 )
1770 })
1771 .collect();
1772 self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
1773 .unwrap();
1774 cx.emit(Event::Rejoined);
1775 cx.notify();
1776 Ok(())
1777 }
1778
1779 pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
1780 self.unshare_internal(cx)?;
1781 self.metadata_changed(cx);
1782 cx.notify();
1783 Ok(())
1784 }
1785
1786 fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
1787 if self.is_via_collab() {
1788 if self.dev_server_project_id().is_some() {
1789 if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1790 *in_room = false
1791 }
1792 return Ok(());
1793 } else {
1794 return Err(anyhow!("attempted to unshare a remote project"));
1795 }
1796 }
1797
1798 if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
1799 self.client_state = ProjectClientState::Local;
1800 self.collaborators.clear();
1801 self.shared_buffers.clear();
1802 self.client_subscriptions.clear();
1803 self.worktree_store.update(cx, |store, cx| {
1804 store.set_shared(false, cx);
1805 });
1806 self.buffer_store
1807 .update(cx, |buffer_store, cx| buffer_store.set_remote_id(None, cx));
1808 self.client
1809 .send(proto::UnshareProject {
1810 project_id: remote_id,
1811 })
1812 .ok();
1813 Ok(())
1814 } else {
1815 Err(anyhow!("attempted to unshare an unshared project"))
1816 }
1817 }
1818
1819 pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
1820 if self.is_disconnected() {
1821 return;
1822 }
1823 self.disconnected_from_host_internal(cx);
1824 cx.emit(Event::DisconnectedFromHost);
1825 cx.notify();
1826 }
1827
1828 pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut ModelContext<Self>) {
1829 let new_capability =
1830 if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
1831 Capability::ReadWrite
1832 } else {
1833 Capability::ReadOnly
1834 };
1835 if let ProjectClientState::Remote { capability, .. } = &mut self.client_state {
1836 if *capability == new_capability {
1837 return;
1838 }
1839
1840 *capability = new_capability;
1841 for buffer in self.opened_buffers(cx) {
1842 buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
1843 }
1844 }
1845 }
1846
1847 fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
1848 if let ProjectClientState::Remote {
1849 sharing_has_stopped,
1850 ..
1851 } = &mut self.client_state
1852 {
1853 *sharing_has_stopped = true;
1854 self.collaborators.clear();
1855 self.worktree_store.update(cx, |store, cx| {
1856 store.disconnected_from_host(cx);
1857 });
1858 self.buffer_store.update(cx, |buffer_store, cx| {
1859 buffer_store.disconnected_from_host(cx)
1860 });
1861 }
1862 }
1863
1864 pub fn close(&mut self, cx: &mut ModelContext<Self>) {
1865 cx.emit(Event::Closed);
1866 }
1867
1868 pub fn is_disconnected(&self) -> bool {
1869 match &self.client_state {
1870 ProjectClientState::Remote {
1871 sharing_has_stopped,
1872 ..
1873 } => *sharing_has_stopped,
1874 _ => false,
1875 }
1876 }
1877
1878 pub fn capability(&self) -> Capability {
1879 match &self.client_state {
1880 ProjectClientState::Remote { capability, .. } => *capability,
1881 ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
1882 }
1883 }
1884
1885 pub fn is_read_only(&self) -> bool {
1886 self.is_disconnected() || self.capability() == Capability::ReadOnly
1887 }
1888
1889 pub fn is_local(&self) -> bool {
1890 match &self.client_state {
1891 ProjectClientState::Local | ProjectClientState::Shared { .. } => {
1892 self.ssh_session.is_none()
1893 }
1894 ProjectClientState::Remote { .. } => false,
1895 }
1896 }
1897
1898 pub fn is_local_or_ssh(&self) -> bool {
1899 match &self.client_state {
1900 ProjectClientState::Local | ProjectClientState::Shared { .. } => true,
1901 ProjectClientState::Remote { .. } => false,
1902 }
1903 }
1904
1905 pub fn is_via_collab(&self) -> bool {
1906 match &self.client_state {
1907 ProjectClientState::Local | ProjectClientState::Shared { .. } => false,
1908 ProjectClientState::Remote { .. } => true,
1909 }
1910 }
1911
1912 pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<Model<Buffer>>> {
1913 self.buffer_store.update(cx, |buffer_store, cx| {
1914 buffer_store.create_buffer(
1915 if self.is_via_collab() {
1916 Some((self.client.clone().into(), self.remote_id().unwrap()))
1917 } else {
1918 None
1919 },
1920 cx,
1921 )
1922 })
1923 }
1924
1925 pub fn create_local_buffer(
1926 &mut self,
1927 text: &str,
1928 language: Option<Arc<Language>>,
1929 cx: &mut ModelContext<Self>,
1930 ) -> Model<Buffer> {
1931 if self.is_via_collab() {
1932 panic!("called create_local_buffer on a remote project")
1933 }
1934 self.buffer_store.update(cx, |buffer_store, cx| {
1935 buffer_store.create_local_buffer(text, language, cx)
1936 })
1937 }
1938
1939 pub fn open_path(
1940 &mut self,
1941 path: ProjectPath,
1942 cx: &mut ModelContext<Self>,
1943 ) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
1944 let task = self.open_buffer(path.clone(), cx);
1945 cx.spawn(move |_, cx| async move {
1946 let buffer = task.await?;
1947 let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
1948 File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1949 })?;
1950
1951 let buffer: &AnyModel = &buffer;
1952 Ok((project_entry_id, buffer.clone()))
1953 })
1954 }
1955
1956 pub fn open_local_buffer(
1957 &mut self,
1958 abs_path: impl AsRef<Path>,
1959 cx: &mut ModelContext<Self>,
1960 ) -> Task<Result<Model<Buffer>>> {
1961 if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
1962 self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1963 } else {
1964 Task::ready(Err(anyhow!("no such path")))
1965 }
1966 }
1967
1968 pub fn open_buffer(
1969 &mut self,
1970 path: impl Into<ProjectPath>,
1971 cx: &mut ModelContext<Self>,
1972 ) -> Task<Result<Model<Buffer>>> {
1973 if self.is_via_collab() && self.is_disconnected() {
1974 return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
1975 }
1976
1977 self.buffer_store.update(cx, |buffer_store, cx| {
1978 buffer_store.open_buffer(path.into(), cx)
1979 })
1980 }
1981
1982 pub fn open_local_buffer_via_lsp(
1983 &mut self,
1984 mut abs_path: lsp::Url,
1985 language_server_id: LanguageServerId,
1986 language_server_name: LanguageServerName,
1987 cx: &mut ModelContext<Self>,
1988 ) -> Task<Result<Model<Buffer>>> {
1989 cx.spawn(move |this, mut cx| async move {
1990 // Escape percent-encoded string.
1991 let current_scheme = abs_path.scheme().to_owned();
1992 let _ = abs_path.set_scheme("file");
1993
1994 let abs_path = abs_path
1995 .to_file_path()
1996 .map_err(|_| anyhow!("can't convert URI to path"))?;
1997 let p = abs_path.clone();
1998 let yarn_worktree = this
1999 .update(&mut cx, move |this, cx| {
2000 this.yarn.update(cx, |_, cx| {
2001 cx.spawn(|this, mut cx| async move {
2002 let t = this
2003 .update(&mut cx, |this, cx| {
2004 this.process_path(&p, ¤t_scheme, cx)
2005 })
2006 .ok()?;
2007 t.await
2008 })
2009 })
2010 })?
2011 .await;
2012 let (worktree_root_target, known_relative_path) =
2013 if let Some((zip_root, relative_path)) = yarn_worktree {
2014 (zip_root, Some(relative_path))
2015 } else {
2016 (Arc::<Path>::from(abs_path.as_path()), None)
2017 };
2018 let (worktree, relative_path) = if let Some(result) = this
2019 .update(&mut cx, |this, cx| {
2020 this.find_worktree(&worktree_root_target, cx)
2021 })? {
2022 let relative_path =
2023 known_relative_path.unwrap_or_else(|| Arc::<Path>::from(result.1));
2024 (result.0, relative_path)
2025 } else {
2026 let worktree = this
2027 .update(&mut cx, |this, cx| {
2028 this.create_worktree(&worktree_root_target, false, cx)
2029 })?
2030 .await?;
2031 this.update(&mut cx, |this, cx| {
2032 this.language_server_ids.insert(
2033 (worktree.read(cx).id(), language_server_name),
2034 language_server_id,
2035 );
2036 })
2037 .ok();
2038 let worktree_root = worktree.update(&mut cx, |this, _| this.abs_path())?;
2039 let relative_path = if let Some(known_path) = known_relative_path {
2040 known_path
2041 } else {
2042 abs_path.strip_prefix(worktree_root)?.into()
2043 };
2044 (worktree, relative_path)
2045 };
2046 let project_path = ProjectPath {
2047 worktree_id: worktree.update(&mut cx, |worktree, _| worktree.id())?,
2048 path: relative_path,
2049 };
2050 this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))?
2051 .await
2052 })
2053 }
2054
2055 pub fn open_buffer_by_id(
2056 &mut self,
2057 id: BufferId,
2058 cx: &mut ModelContext<Self>,
2059 ) -> Task<Result<Model<Buffer>>> {
2060 if let Some(buffer) = self.buffer_for_id(id, cx) {
2061 Task::ready(Ok(buffer))
2062 } else if self.is_local_or_ssh() {
2063 Task::ready(Err(anyhow!("buffer {} does not exist", id)))
2064 } else if let Some(project_id) = self.remote_id() {
2065 let request = self.client.request(proto::OpenBufferById {
2066 project_id,
2067 id: id.into(),
2068 });
2069 cx.spawn(move |this, mut cx| async move {
2070 let buffer_id = BufferId::new(request.await?.buffer_id)?;
2071 this.update(&mut cx, |this, cx| {
2072 this.wait_for_remote_buffer(buffer_id, cx)
2073 })?
2074 .await
2075 })
2076 } else {
2077 Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
2078 }
2079 }
2080
2081 pub fn save_buffers(
2082 &self,
2083 buffers: HashSet<Model<Buffer>>,
2084 cx: &mut ModelContext<Self>,
2085 ) -> Task<Result<()>> {
2086 cx.spawn(move |this, mut cx| async move {
2087 let save_tasks = buffers.into_iter().filter_map(|buffer| {
2088 this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx))
2089 .ok()
2090 });
2091 try_join_all(save_tasks).await?;
2092 Ok(())
2093 })
2094 }
2095
2096 pub fn save_buffer(
2097 &self,
2098 buffer: Model<Buffer>,
2099 cx: &mut ModelContext<Self>,
2100 ) -> Task<Result<()>> {
2101 self.buffer_store
2102 .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
2103 }
2104
2105 pub fn save_buffer_as(
2106 &mut self,
2107 buffer: Model<Buffer>,
2108 path: ProjectPath,
2109 cx: &mut ModelContext<Self>,
2110 ) -> Task<Result<()>> {
2111 self.buffer_store.update(cx, |buffer_store, cx| {
2112 buffer_store.save_buffer_as(buffer.clone(), path, cx)
2113 })
2114 }
2115
2116 pub fn get_open_buffer(
2117 &mut self,
2118 path: &ProjectPath,
2119 cx: &mut ModelContext<Self>,
2120 ) -> Option<Model<Buffer>> {
2121 self.buffer_store.read(cx).get_by_path(path, cx)
2122 }
2123
2124 fn register_buffer(
2125 &mut self,
2126 buffer: &Model<Buffer>,
2127 cx: &mut ModelContext<Self>,
2128 ) -> Result<()> {
2129 {
2130 let mut remotely_created_buffers = self.remotely_created_buffers.lock();
2131 if remotely_created_buffers.retain_count > 0 {
2132 remotely_created_buffers.buffers.push(buffer.clone())
2133 }
2134 }
2135
2136 self.request_buffer_diff_recalculation(buffer, cx);
2137 buffer.update(cx, |buffer, _| {
2138 buffer.set_language_registry(self.languages.clone())
2139 });
2140
2141 cx.subscribe(buffer, |this, buffer, event, cx| {
2142 this.on_buffer_event(buffer, event, cx);
2143 })
2144 .detach();
2145
2146 self.detect_language_for_buffer(buffer, cx);
2147 self.register_buffer_with_language_servers(buffer, cx);
2148 cx.observe_release(buffer, |this, buffer, cx| {
2149 if let Some(file) = File::from_dyn(buffer.file()) {
2150 if file.is_local() {
2151 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
2152 for server in this.language_servers_for_buffer(buffer, cx) {
2153 server
2154 .1
2155 .notify::<lsp::notification::DidCloseTextDocument>(
2156 lsp::DidCloseTextDocumentParams {
2157 text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
2158 },
2159 )
2160 .log_err();
2161 }
2162 }
2163 }
2164 })
2165 .detach();
2166
2167 Ok(())
2168 }
2169
2170 fn register_buffer_with_language_servers(
2171 &mut self,
2172 buffer_handle: &Model<Buffer>,
2173 cx: &mut ModelContext<Self>,
2174 ) {
2175 let buffer = buffer_handle.read(cx);
2176 let buffer_id = buffer.remote_id();
2177
2178 if let Some(file) = File::from_dyn(buffer.file()) {
2179 if !file.is_local() {
2180 return;
2181 }
2182
2183 let abs_path = file.abs_path(cx);
2184 let Some(uri) = lsp::Url::from_file_path(&abs_path).log_err() else {
2185 return;
2186 };
2187 let initial_snapshot = buffer.text_snapshot();
2188 let language = buffer.language().cloned();
2189 let worktree_id = file.worktree_id(cx);
2190
2191 if let Some(diagnostics) = self.diagnostics.get(&worktree_id) {
2192 for (server_id, diagnostics) in
2193 diagnostics.get(file.path()).cloned().unwrap_or_default()
2194 {
2195 self.update_buffer_diagnostics(buffer_handle, server_id, None, diagnostics, cx)
2196 .log_err();
2197 }
2198 }
2199
2200 if let Some(language) = language {
2201 for adapter in self.languages.lsp_adapters(&language) {
2202 let server = self
2203 .language_server_ids
2204 .get(&(worktree_id, adapter.name.clone()))
2205 .and_then(|id| self.language_servers.get(id))
2206 .and_then(|server_state| {
2207 if let LanguageServerState::Running { server, .. } = server_state {
2208 Some(server.clone())
2209 } else {
2210 None
2211 }
2212 });
2213 let server = match server {
2214 Some(server) => server,
2215 None => continue,
2216 };
2217
2218 server
2219 .notify::<lsp::notification::DidOpenTextDocument>(
2220 lsp::DidOpenTextDocumentParams {
2221 text_document: lsp::TextDocumentItem::new(
2222 uri.clone(),
2223 adapter.language_id(&language),
2224 0,
2225 initial_snapshot.text(),
2226 ),
2227 },
2228 )
2229 .log_err();
2230
2231 buffer_handle.update(cx, |buffer, cx| {
2232 buffer.set_completion_triggers(
2233 server
2234 .capabilities()
2235 .completion_provider
2236 .as_ref()
2237 .and_then(|provider| provider.trigger_characters.clone())
2238 .unwrap_or_default(),
2239 cx,
2240 );
2241 });
2242
2243 let snapshot = LspBufferSnapshot {
2244 version: 0,
2245 snapshot: initial_snapshot.clone(),
2246 };
2247 self.buffer_snapshots
2248 .entry(buffer_id)
2249 .or_default()
2250 .insert(server.server_id(), vec![snapshot]);
2251 }
2252 }
2253 }
2254 }
2255
2256 fn unregister_buffer_from_language_servers(
2257 &mut self,
2258 buffer: &Model<Buffer>,
2259 old_file: &File,
2260 cx: &mut AppContext,
2261 ) {
2262 let old_path = match old_file.as_local() {
2263 Some(local) => local.abs_path(cx),
2264 None => return,
2265 };
2266
2267 buffer.update(cx, |buffer, cx| {
2268 let worktree_id = old_file.worktree_id(cx);
2269
2270 let ids = &self.language_server_ids;
2271
2272 if let Some(language) = buffer.language().cloned() {
2273 for adapter in self.languages.lsp_adapters(&language) {
2274 if let Some(server_id) = ids.get(&(worktree_id, adapter.name.clone())) {
2275 buffer.update_diagnostics(*server_id, Default::default(), cx);
2276 }
2277 }
2278 }
2279
2280 self.buffer_snapshots.remove(&buffer.remote_id());
2281 let file_url = lsp::Url::from_file_path(old_path).unwrap();
2282 for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
2283 language_server
2284 .notify::<lsp::notification::DidCloseTextDocument>(
2285 lsp::DidCloseTextDocumentParams {
2286 text_document: lsp::TextDocumentIdentifier::new(file_url.clone()),
2287 },
2288 )
2289 .log_err();
2290 }
2291 });
2292 }
2293
2294 async fn send_buffer_ordered_messages(
2295 this: WeakModel<Self>,
2296 rx: UnboundedReceiver<BufferOrderedMessage>,
2297 mut cx: AsyncAppContext,
2298 ) -> Result<()> {
2299 const MAX_BATCH_SIZE: usize = 128;
2300
2301 let mut operations_by_buffer_id = HashMap::default();
2302 async fn flush_operations(
2303 this: &WeakModel<Project>,
2304 operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
2305 needs_resync_with_host: &mut bool,
2306 is_local: bool,
2307 cx: &mut AsyncAppContext,
2308 ) -> Result<()> {
2309 for (buffer_id, operations) in operations_by_buffer_id.drain() {
2310 let request = this.update(cx, |this, _| {
2311 let project_id = this.remote_id()?;
2312 Some(this.client.request(proto::UpdateBuffer {
2313 buffer_id: buffer_id.into(),
2314 project_id,
2315 operations,
2316 }))
2317 })?;
2318 if let Some(request) = request {
2319 if request.await.is_err() && !is_local {
2320 *needs_resync_with_host = true;
2321 break;
2322 }
2323 }
2324 }
2325 Ok(())
2326 }
2327
2328 let mut needs_resync_with_host = false;
2329 let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
2330
2331 while let Some(changes) = changes.next().await {
2332 let is_local = this.update(&mut cx, |this, _| this.is_local_or_ssh())?;
2333
2334 for change in changes {
2335 match change {
2336 BufferOrderedMessage::Operation {
2337 buffer_id,
2338 operation,
2339 } => {
2340 if needs_resync_with_host {
2341 continue;
2342 }
2343
2344 operations_by_buffer_id
2345 .entry(buffer_id)
2346 .or_insert(Vec::new())
2347 .push(operation);
2348 }
2349
2350 BufferOrderedMessage::Resync => {
2351 operations_by_buffer_id.clear();
2352 if this
2353 .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))?
2354 .await
2355 .is_ok()
2356 {
2357 needs_resync_with_host = false;
2358 }
2359 }
2360
2361 BufferOrderedMessage::LanguageServerUpdate {
2362 language_server_id,
2363 message,
2364 } => {
2365 flush_operations(
2366 &this,
2367 &mut operations_by_buffer_id,
2368 &mut needs_resync_with_host,
2369 is_local,
2370 &mut cx,
2371 )
2372 .await?;
2373
2374 this.update(&mut cx, |this, _| {
2375 if let Some(project_id) = this.remote_id() {
2376 this.client
2377 .send(proto::UpdateLanguageServer {
2378 project_id,
2379 language_server_id: language_server_id.0 as u64,
2380 variant: Some(message),
2381 })
2382 .log_err();
2383 }
2384 })?;
2385 }
2386 }
2387 }
2388
2389 flush_operations(
2390 &this,
2391 &mut operations_by_buffer_id,
2392 &mut needs_resync_with_host,
2393 is_local,
2394 &mut cx,
2395 )
2396 .await?;
2397 }
2398
2399 Ok(())
2400 }
2401
2402 fn on_buffer_store_event(
2403 &mut self,
2404 _: Model<BufferStore>,
2405 event: &BufferStoreEvent,
2406 cx: &mut ModelContext<Self>,
2407 ) {
2408 match event {
2409 BufferStoreEvent::BufferAdded(buffer) => {
2410 self.register_buffer(buffer, cx).log_err();
2411 }
2412 BufferStoreEvent::BufferChangedFilePath { buffer, old_file } => {
2413 if let Some(old_file) = File::from_dyn(old_file.as_ref()) {
2414 self.unregister_buffer_from_language_servers(&buffer, old_file, cx);
2415 }
2416
2417 self.detect_language_for_buffer(&buffer, cx);
2418 self.register_buffer_with_language_servers(&buffer, cx);
2419 }
2420 BufferStoreEvent::MessageToReplicas(message) => {
2421 self.client.send_dynamic(message.as_ref().clone()).log_err();
2422 }
2423 }
2424 }
2425
2426 fn on_worktree_store_event(
2427 &mut self,
2428 _: Model<WorktreeStore>,
2429 event: &WorktreeStoreEvent,
2430 cx: &mut ModelContext<Self>,
2431 ) {
2432 match event {
2433 WorktreeStoreEvent::WorktreeAdded(_) => cx.emit(Event::WorktreeAdded),
2434 WorktreeStoreEvent::WorktreeRemoved(_, id) => cx.emit(Event::WorktreeRemoved(*id)),
2435 WorktreeStoreEvent::WorktreeOrderChanged => cx.emit(Event::WorktreeOrderChanged),
2436 }
2437 }
2438
2439 fn on_buffer_event(
2440 &mut self,
2441 buffer: Model<Buffer>,
2442 event: &BufferEvent,
2443 cx: &mut ModelContext<Self>,
2444 ) -> Option<()> {
2445 if matches!(
2446 event,
2447 BufferEvent::Edited { .. } | BufferEvent::Reloaded | BufferEvent::DiffBaseChanged
2448 ) {
2449 self.request_buffer_diff_recalculation(&buffer, cx);
2450 }
2451
2452 let buffer_id = buffer.read(cx).remote_id();
2453 match event {
2454 BufferEvent::Operation(operation) => {
2455 let operation = language::proto::serialize_operation(operation);
2456
2457 if let Some(ssh) = &self.ssh_session {
2458 ssh.send(proto::UpdateBuffer {
2459 project_id: 0,
2460 buffer_id: buffer_id.to_proto(),
2461 operations: vec![operation.clone()],
2462 })
2463 .ok();
2464 }
2465
2466 self.enqueue_buffer_ordered_message(BufferOrderedMessage::Operation {
2467 buffer_id,
2468 operation,
2469 })
2470 .ok();
2471 }
2472
2473 BufferEvent::Reloaded => {
2474 if self.is_local_or_ssh() {
2475 if let Some(project_id) = self.remote_id() {
2476 let buffer = buffer.read(cx);
2477 self.client
2478 .send(proto::BufferReloaded {
2479 project_id,
2480 buffer_id: buffer.remote_id().to_proto(),
2481 version: serialize_version(&buffer.version()),
2482 mtime: buffer.saved_mtime().map(|t| t.into()),
2483 line_ending: serialize_line_ending(buffer.line_ending()) as i32,
2484 })
2485 .log_err();
2486 }
2487 }
2488 }
2489
2490 BufferEvent::Edited { .. } => {
2491 let buffer = buffer.read(cx);
2492 let file = File::from_dyn(buffer.file())?;
2493 let abs_path = file.as_local()?.abs_path(cx);
2494 let uri = lsp::Url::from_file_path(abs_path).unwrap();
2495 let next_snapshot = buffer.text_snapshot();
2496
2497 let language_servers: Vec<_> = self
2498 .language_servers_for_buffer(buffer, cx)
2499 .map(|i| i.1.clone())
2500 .collect();
2501
2502 for language_server in language_servers {
2503 let language_server = language_server.clone();
2504
2505 let buffer_snapshots = self
2506 .buffer_snapshots
2507 .get_mut(&buffer.remote_id())
2508 .and_then(|m| m.get_mut(&language_server.server_id()))?;
2509 let previous_snapshot = buffer_snapshots.last()?;
2510
2511 let build_incremental_change = || {
2512 buffer
2513 .edits_since::<(PointUtf16, usize)>(
2514 previous_snapshot.snapshot.version(),
2515 )
2516 .map(|edit| {
2517 let edit_start = edit.new.start.0;
2518 let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
2519 let new_text = next_snapshot
2520 .text_for_range(edit.new.start.1..edit.new.end.1)
2521 .collect();
2522 lsp::TextDocumentContentChangeEvent {
2523 range: Some(lsp::Range::new(
2524 point_to_lsp(edit_start),
2525 point_to_lsp(edit_end),
2526 )),
2527 range_length: None,
2528 text: new_text,
2529 }
2530 })
2531 .collect()
2532 };
2533
2534 let document_sync_kind = language_server
2535 .capabilities()
2536 .text_document_sync
2537 .as_ref()
2538 .and_then(|sync| match sync {
2539 lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
2540 lsp::TextDocumentSyncCapability::Options(options) => options.change,
2541 });
2542
2543 let content_changes: Vec<_> = match document_sync_kind {
2544 Some(lsp::TextDocumentSyncKind::FULL) => {
2545 vec![lsp::TextDocumentContentChangeEvent {
2546 range: None,
2547 range_length: None,
2548 text: next_snapshot.text(),
2549 }]
2550 }
2551 Some(lsp::TextDocumentSyncKind::INCREMENTAL) => build_incremental_change(),
2552 _ => {
2553 #[cfg(any(test, feature = "test-support"))]
2554 {
2555 build_incremental_change()
2556 }
2557
2558 #[cfg(not(any(test, feature = "test-support")))]
2559 {
2560 continue;
2561 }
2562 }
2563 };
2564
2565 let next_version = previous_snapshot.version + 1;
2566 buffer_snapshots.push(LspBufferSnapshot {
2567 version: next_version,
2568 snapshot: next_snapshot.clone(),
2569 });
2570
2571 language_server
2572 .notify::<lsp::notification::DidChangeTextDocument>(
2573 lsp::DidChangeTextDocumentParams {
2574 text_document: lsp::VersionedTextDocumentIdentifier::new(
2575 uri.clone(),
2576 next_version,
2577 ),
2578 content_changes,
2579 },
2580 )
2581 .log_err();
2582 }
2583 }
2584
2585 BufferEvent::Saved => {
2586 let file = File::from_dyn(buffer.read(cx).file())?;
2587 let worktree_id = file.worktree_id(cx);
2588 let abs_path = file.as_local()?.abs_path(cx);
2589 let text_document = lsp::TextDocumentIdentifier {
2590 uri: lsp::Url::from_file_path(abs_path).log_err()?,
2591 };
2592
2593 for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
2594 if let Some(include_text) = include_text(server.as_ref()) {
2595 let text = if include_text {
2596 Some(buffer.read(cx).text())
2597 } else {
2598 None
2599 };
2600 server
2601 .notify::<lsp::notification::DidSaveTextDocument>(
2602 lsp::DidSaveTextDocumentParams {
2603 text_document: text_document.clone(),
2604 text,
2605 },
2606 )
2607 .log_err();
2608 }
2609 }
2610
2611 for language_server_id in self.language_server_ids_for_buffer(buffer.read(cx), cx) {
2612 self.simulate_disk_based_diagnostics_events_if_needed(language_server_id, cx);
2613 }
2614 }
2615
2616 _ => {}
2617 }
2618
2619 None
2620 }
2621
2622 // After saving a buffer using a language server that doesn't provide a disk-based progress token,
2623 // kick off a timer that will reset every time the buffer is saved. If the timer eventually fires,
2624 // simulate disk-based diagnostics being finished so that other pieces of UI (e.g., project
2625 // diagnostics view, diagnostic status bar) can update. We don't emit an event right away because
2626 // the language server might take some time to publish diagnostics.
2627 fn simulate_disk_based_diagnostics_events_if_needed(
2628 &mut self,
2629 language_server_id: LanguageServerId,
2630 cx: &mut ModelContext<Self>,
2631 ) {
2632 const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration = Duration::from_secs(1);
2633
2634 let Some(LanguageServerState::Running {
2635 simulate_disk_based_diagnostics_completion,
2636 adapter,
2637 ..
2638 }) = self.language_servers.get_mut(&language_server_id)
2639 else {
2640 return;
2641 };
2642
2643 if adapter.disk_based_diagnostics_progress_token.is_some() {
2644 return;
2645 }
2646
2647 let prev_task = simulate_disk_based_diagnostics_completion.replace(cx.spawn(
2648 move |this, mut cx| async move {
2649 cx.background_executor()
2650 .timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE)
2651 .await;
2652
2653 this.update(&mut cx, |this, cx| {
2654 this.disk_based_diagnostics_finished(language_server_id, cx);
2655
2656 if let Some(LanguageServerState::Running {
2657 simulate_disk_based_diagnostics_completion,
2658 ..
2659 }) = this.language_servers.get_mut(&language_server_id)
2660 {
2661 *simulate_disk_based_diagnostics_completion = None;
2662 }
2663 })
2664 .ok();
2665 },
2666 ));
2667
2668 if prev_task.is_none() {
2669 self.disk_based_diagnostics_started(language_server_id, cx);
2670 }
2671 }
2672
2673 fn request_buffer_diff_recalculation(
2674 &mut self,
2675 buffer: &Model<Buffer>,
2676 cx: &mut ModelContext<Self>,
2677 ) {
2678 self.buffers_needing_diff.insert(buffer.downgrade());
2679 let first_insertion = self.buffers_needing_diff.len() == 1;
2680
2681 let settings = ProjectSettings::get_global(cx);
2682 let delay = if let Some(delay) = settings.git.gutter_debounce {
2683 delay
2684 } else {
2685 if first_insertion {
2686 let this = cx.weak_model();
2687 cx.defer(move |cx| {
2688 if let Some(this) = this.upgrade() {
2689 this.update(cx, |this, cx| {
2690 this.recalculate_buffer_diffs(cx).detach();
2691 });
2692 }
2693 });
2694 }
2695 return;
2696 };
2697
2698 const MIN_DELAY: u64 = 50;
2699 let delay = delay.max(MIN_DELAY);
2700 let duration = Duration::from_millis(delay);
2701
2702 self.git_diff_debouncer
2703 .fire_new(duration, cx, move |this, cx| {
2704 this.recalculate_buffer_diffs(cx)
2705 });
2706 }
2707
2708 fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
2709 let buffers = self.buffers_needing_diff.drain().collect::<Vec<_>>();
2710 cx.spawn(move |this, mut cx| async move {
2711 let tasks: Vec<_> = buffers
2712 .iter()
2713 .filter_map(|buffer| {
2714 let buffer = buffer.upgrade()?;
2715 buffer
2716 .update(&mut cx, |buffer, cx| buffer.git_diff_recalc(cx))
2717 .ok()
2718 .flatten()
2719 })
2720 .collect();
2721
2722 futures::future::join_all(tasks).await;
2723
2724 this.update(&mut cx, |this, cx| {
2725 if this.buffers_needing_diff.is_empty() {
2726 // TODO: Would a `ModelContext<Project>.notify()` suffice here?
2727 for buffer in buffers {
2728 if let Some(buffer) = buffer.upgrade() {
2729 buffer.update(cx, |_, cx| cx.notify());
2730 }
2731 }
2732 } else {
2733 this.recalculate_buffer_diffs(cx).detach();
2734 }
2735 })
2736 .ok();
2737 })
2738 }
2739
2740 fn language_servers_for_worktree(
2741 &self,
2742 worktree_id: WorktreeId,
2743 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<Language>, &Arc<LanguageServer>)> {
2744 self.language_server_ids
2745 .iter()
2746 .filter_map(move |((language_server_worktree_id, _), id)| {
2747 if *language_server_worktree_id == worktree_id {
2748 if let Some(LanguageServerState::Running {
2749 adapter,
2750 language,
2751 server,
2752 ..
2753 }) = self.language_servers.get(id)
2754 {
2755 return Some((adapter, language, server));
2756 }
2757 }
2758 None
2759 })
2760 }
2761
2762 fn maintain_buffer_languages(
2763 languages: Arc<LanguageRegistry>,
2764 cx: &mut ModelContext<Project>,
2765 ) -> Task<()> {
2766 let mut subscription = languages.subscribe();
2767 let mut prev_reload_count = languages.reload_count();
2768 cx.spawn(move |project, mut cx| async move {
2769 while let Some(()) = subscription.next().await {
2770 if let Some(project) = project.upgrade() {
2771 // If the language registry has been reloaded, then remove and
2772 // re-assign the languages on all open buffers.
2773 let reload_count = languages.reload_count();
2774 if reload_count > prev_reload_count {
2775 prev_reload_count = reload_count;
2776 project
2777 .update(&mut cx, |this, cx| {
2778 this.buffer_store.clone().update(cx, |buffer_store, cx| {
2779 for buffer in buffer_store.buffers() {
2780 if let Some(f) =
2781 File::from_dyn(buffer.read(cx).file()).cloned()
2782 {
2783 this.unregister_buffer_from_language_servers(
2784 &buffer, &f, cx,
2785 );
2786 buffer.update(cx, |buffer, cx| {
2787 buffer.set_language(None, cx)
2788 });
2789 }
2790 }
2791 });
2792 })
2793 .ok();
2794 }
2795
2796 project
2797 .update(&mut cx, |project, cx| {
2798 let mut plain_text_buffers = Vec::new();
2799 let mut buffers_with_unknown_injections = Vec::new();
2800 for handle in project.buffer_store.read(cx).buffers() {
2801 let buffer = handle.read(cx);
2802 if buffer.language().is_none()
2803 || buffer.language() == Some(&*language::PLAIN_TEXT)
2804 {
2805 plain_text_buffers.push(handle);
2806 } else if buffer.contains_unknown_injections() {
2807 buffers_with_unknown_injections.push(handle);
2808 }
2809 }
2810
2811 for buffer in plain_text_buffers {
2812 project.detect_language_for_buffer(&buffer, cx);
2813 project.register_buffer_with_language_servers(&buffer, cx);
2814 }
2815
2816 for buffer in buffers_with_unknown_injections {
2817 buffer.update(cx, |buffer, cx| buffer.reparse(cx));
2818 }
2819 })
2820 .ok();
2821 }
2822 }
2823 })
2824 }
2825
2826 fn maintain_workspace_config(cx: &mut ModelContext<Project>) -> Task<Result<()>> {
2827 let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
2828 let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
2829
2830 let settings_observation = cx.observe_global::<SettingsStore>(move |_, _| {
2831 *settings_changed_tx.borrow_mut() = ();
2832 });
2833
2834 cx.spawn(move |this, mut cx| async move {
2835 while let Some(()) = settings_changed_rx.next().await {
2836 let servers = this.update(&mut cx, |this, cx| {
2837 this.language_server_ids
2838 .iter()
2839 .filter_map(|((worktree_id, _), server_id)| {
2840 let worktree = this.worktree_for_id(*worktree_id, cx)?;
2841 let state = this.language_servers.get(server_id)?;
2842 let delegate = ProjectLspAdapterDelegate::new(this, &worktree, cx);
2843 match state {
2844 LanguageServerState::Starting(_) => None,
2845 LanguageServerState::Running {
2846 adapter, server, ..
2847 } => Some((
2848 adapter.adapter.clone(),
2849 server.clone(),
2850 delegate as Arc<dyn LspAdapterDelegate>,
2851 )),
2852 }
2853 })
2854 .collect::<Vec<_>>()
2855 })?;
2856
2857 for (adapter, server, delegate) in servers {
2858 let settings = adapter.workspace_configuration(&delegate, &mut cx).await?;
2859
2860 server
2861 .notify::<lsp::notification::DidChangeConfiguration>(
2862 lsp::DidChangeConfigurationParams { settings },
2863 )
2864 .ok();
2865 }
2866 }
2867
2868 drop(settings_observation);
2869 anyhow::Ok(())
2870 })
2871 }
2872
2873 fn detect_language_for_buffer(
2874 &mut self,
2875 buffer_handle: &Model<Buffer>,
2876 cx: &mut ModelContext<Self>,
2877 ) {
2878 // If the buffer has a language, set it and start the language server if we haven't already.
2879 let buffer = buffer_handle.read(cx);
2880 let Some(file) = buffer.file() else {
2881 return;
2882 };
2883 let content = buffer.as_rope();
2884 let Some(new_language_result) = self
2885 .languages
2886 .language_for_file(file, Some(content), cx)
2887 .now_or_never()
2888 else {
2889 return;
2890 };
2891
2892 match new_language_result {
2893 Err(e) => {
2894 if e.is::<language::LanguageNotFound>() {
2895 cx.emit(Event::LanguageNotFound(buffer_handle.clone()))
2896 }
2897 }
2898 Ok(new_language) => {
2899 self.set_language_for_buffer(buffer_handle, new_language, cx);
2900 }
2901 };
2902 }
2903
2904 pub fn set_language_for_buffer(
2905 &mut self,
2906 buffer: &Model<Buffer>,
2907 new_language: Arc<Language>,
2908 cx: &mut ModelContext<Self>,
2909 ) {
2910 buffer.update(cx, |buffer, cx| {
2911 if buffer.language().map_or(true, |old_language| {
2912 !Arc::ptr_eq(old_language, &new_language)
2913 }) {
2914 buffer.set_language(Some(new_language.clone()), cx);
2915 }
2916 });
2917
2918 let buffer_file = buffer.read(cx).file().cloned();
2919 let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
2920 let buffer_file = File::from_dyn(buffer_file.as_ref());
2921 let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
2922 if let Some(prettier_plugins) = prettier_support::prettier_plugins_for_language(&settings) {
2923 self.install_default_prettier(
2924 worktree,
2925 prettier_plugins.iter().map(|s| Arc::from(s.as_str())),
2926 cx,
2927 );
2928 };
2929 if let Some(file) = buffer_file {
2930 let worktree = file.worktree.clone();
2931 if worktree.read(cx).is_local() {
2932 self.start_language_servers(&worktree, new_language, cx);
2933 }
2934 }
2935 }
2936
2937 fn start_language_servers(
2938 &mut self,
2939 worktree: &Model<Worktree>,
2940 language: Arc<Language>,
2941 cx: &mut ModelContext<Self>,
2942 ) {
2943 let (root_file, is_local) =
2944 worktree.update(cx, |tree, cx| (tree.root_file(cx), tree.is_local()));
2945 let settings = language_settings(Some(&language), root_file.map(|f| f as _).as_ref(), cx);
2946 if !settings.enable_language_server || !is_local {
2947 return;
2948 }
2949
2950 let available_lsp_adapters = self.languages.clone().lsp_adapters(&language);
2951 let available_language_servers = available_lsp_adapters
2952 .iter()
2953 .map(|lsp_adapter| lsp_adapter.name.clone())
2954 .collect::<Vec<_>>();
2955
2956 let desired_language_servers =
2957 settings.customized_language_servers(&available_language_servers);
2958
2959 let mut enabled_lsp_adapters: Vec<Arc<CachedLspAdapter>> = Vec::new();
2960 for desired_language_server in desired_language_servers {
2961 if let Some(adapter) = available_lsp_adapters
2962 .iter()
2963 .find(|adapter| adapter.name == desired_language_server)
2964 {
2965 enabled_lsp_adapters.push(adapter.clone());
2966 continue;
2967 }
2968
2969 if let Some(adapter) = self
2970 .languages
2971 .load_available_lsp_adapter(&desired_language_server)
2972 {
2973 self.languages()
2974 .register_lsp_adapter(language.name(), adapter.adapter.clone());
2975 enabled_lsp_adapters.push(adapter);
2976 continue;
2977 }
2978
2979 log::warn!(
2980 "no language server found matching '{}'",
2981 desired_language_server.0
2982 );
2983 }
2984
2985 log::info!(
2986 "starting language servers for {language}: {adapters}",
2987 language = language.name(),
2988 adapters = enabled_lsp_adapters
2989 .iter()
2990 .map(|adapter| adapter.name.0.as_ref())
2991 .join(", ")
2992 );
2993
2994 for adapter in &enabled_lsp_adapters {
2995 self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
2996 }
2997
2998 // After starting all the language servers, reorder them to reflect the desired order
2999 // based on the settings.
3000 //
3001 // This is done, in part, to ensure that language servers loaded at different points
3002 // (e.g., native vs extension) still end up in the right order at the end, rather than
3003 // it being based on which language server happened to be loaded in first.
3004 self.languages()
3005 .reorder_language_servers(&language, enabled_lsp_adapters);
3006 }
3007
3008 fn start_language_server(
3009 &mut self,
3010 worktree_handle: &Model<Worktree>,
3011 adapter: Arc<CachedLspAdapter>,
3012 language: Arc<Language>,
3013 cx: &mut ModelContext<Self>,
3014 ) {
3015 if adapter.reinstall_attempt_count.load(SeqCst) > MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
3016 return;
3017 }
3018
3019 let worktree = worktree_handle.read(cx);
3020 let worktree_id = worktree.id();
3021 let worktree_path = worktree.abs_path();
3022 let key = (worktree_id, adapter.name.clone());
3023 if self.language_server_ids.contains_key(&key) {
3024 return;
3025 }
3026
3027 let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
3028 let lsp_adapter_delegate = ProjectLspAdapterDelegate::new(self, worktree_handle, cx);
3029 let pending_server = match self.languages.create_pending_language_server(
3030 stderr_capture.clone(),
3031 language.clone(),
3032 adapter.clone(),
3033 Arc::clone(&worktree_path),
3034 lsp_adapter_delegate.clone(),
3035 cx,
3036 ) {
3037 Some(pending_server) => pending_server,
3038 None => return,
3039 };
3040
3041 let project_settings = ProjectSettings::get(
3042 Some(SettingsLocation {
3043 worktree_id: worktree_id.to_proto() as usize,
3044 path: Path::new(""),
3045 }),
3046 cx,
3047 );
3048 let lsp = project_settings.lsp.get(&adapter.name.0);
3049 let override_options = lsp.and_then(|s| s.initialization_options.clone());
3050
3051 let server_id = pending_server.server_id;
3052 let container_dir = pending_server.container_dir.clone();
3053 let state = LanguageServerState::Starting({
3054 let adapter = adapter.clone();
3055 let server_name = adapter.name.0.clone();
3056 let language = language.clone();
3057 let key = key.clone();
3058
3059 cx.spawn(move |this, mut cx| async move {
3060 let result = Self::setup_and_insert_language_server(
3061 this.clone(),
3062 lsp_adapter_delegate,
3063 override_options,
3064 pending_server,
3065 adapter.clone(),
3066 language.clone(),
3067 server_id,
3068 key,
3069 &mut cx,
3070 )
3071 .await;
3072
3073 match result {
3074 Ok(server) => {
3075 stderr_capture.lock().take();
3076 server
3077 }
3078
3079 Err(err) => {
3080 log::error!("failed to start language server {server_name:?}: {err}");
3081 log::error!("server stderr: {:?}", stderr_capture.lock().take());
3082
3083 let this = this.upgrade()?;
3084 let container_dir = container_dir?;
3085
3086 let attempt_count = adapter.reinstall_attempt_count.fetch_add(1, SeqCst);
3087 if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
3088 let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
3089 log::error!("Hit {max} reinstallation attempts for {server_name:?}");
3090 return None;
3091 }
3092
3093 log::info!(
3094 "retrying installation of language server {server_name:?} in {}s",
3095 SERVER_REINSTALL_DEBOUNCE_TIMEOUT.as_secs()
3096 );
3097 cx.background_executor()
3098 .timer(SERVER_REINSTALL_DEBOUNCE_TIMEOUT)
3099 .await;
3100
3101 let installation_test_binary = adapter
3102 .installation_test_binary(container_dir.to_path_buf())
3103 .await;
3104
3105 this.update(&mut cx, |_, cx| {
3106 Self::check_errored_server(
3107 language,
3108 adapter,
3109 server_id,
3110 installation_test_binary,
3111 cx,
3112 )
3113 })
3114 .ok();
3115
3116 None
3117 }
3118 }
3119 })
3120 });
3121
3122 self.language_servers.insert(server_id, state);
3123 self.language_server_ids.insert(key, server_id);
3124 }
3125
3126 fn reinstall_language_server(
3127 &mut self,
3128 language: Arc<Language>,
3129 adapter: Arc<CachedLspAdapter>,
3130 server_id: LanguageServerId,
3131 cx: &mut ModelContext<Self>,
3132 ) -> Option<Task<()>> {
3133 log::info!("beginning to reinstall server");
3134
3135 let existing_server = match self.language_servers.remove(&server_id) {
3136 Some(LanguageServerState::Running { server, .. }) => Some(server),
3137 _ => None,
3138 };
3139
3140 self.worktree_store.update(cx, |store, cx| {
3141 for worktree in store.worktrees() {
3142 let key = (worktree.read(cx).id(), adapter.name.clone());
3143 self.language_server_ids.remove(&key);
3144 }
3145 });
3146
3147 Some(cx.spawn(move |this, mut cx| async move {
3148 if let Some(task) = existing_server.and_then(|server| server.shutdown()) {
3149 log::info!("shutting down existing server");
3150 task.await;
3151 }
3152
3153 // TODO: This is race-safe with regards to preventing new instances from
3154 // starting while deleting, but existing instances in other projects are going
3155 // to be very confused and messed up
3156 let Some(task) = this
3157 .update(&mut cx, |this, cx| {
3158 this.languages.delete_server_container(adapter.clone(), cx)
3159 })
3160 .log_err()
3161 else {
3162 return;
3163 };
3164 task.await;
3165
3166 this.update(&mut cx, |this, cx| {
3167 for worktree in this.worktree_store.read(cx).worktrees().collect::<Vec<_>>() {
3168 this.start_language_server(&worktree, adapter.clone(), language.clone(), cx);
3169 }
3170 })
3171 .ok();
3172 }))
3173 }
3174
3175 #[allow(clippy::too_many_arguments)]
3176 async fn setup_and_insert_language_server(
3177 this: WeakModel<Self>,
3178 delegate: Arc<dyn LspAdapterDelegate>,
3179 override_initialization_options: Option<serde_json::Value>,
3180 pending_server: PendingLanguageServer,
3181 adapter: Arc<CachedLspAdapter>,
3182 language: Arc<Language>,
3183 server_id: LanguageServerId,
3184 key: (WorktreeId, LanguageServerName),
3185 cx: &mut AsyncAppContext,
3186 ) -> Result<Option<Arc<LanguageServer>>> {
3187 let language_server = Self::setup_pending_language_server(
3188 this.clone(),
3189 override_initialization_options,
3190 pending_server,
3191 delegate,
3192 adapter.clone(),
3193 server_id,
3194 cx,
3195 )
3196 .await?;
3197
3198 let this = match this.upgrade() {
3199 Some(this) => this,
3200 None => return Err(anyhow!("failed to upgrade project handle")),
3201 };
3202
3203 this.update(cx, |this, cx| {
3204 this.insert_newly_running_language_server(
3205 language,
3206 adapter,
3207 language_server.clone(),
3208 server_id,
3209 key,
3210 cx,
3211 )
3212 })??;
3213
3214 Ok(Some(language_server))
3215 }
3216
3217 async fn setup_pending_language_server(
3218 project: WeakModel<Self>,
3219 override_options: Option<serde_json::Value>,
3220 pending_server: PendingLanguageServer,
3221 delegate: Arc<dyn LspAdapterDelegate>,
3222 adapter: Arc<CachedLspAdapter>,
3223 server_id: LanguageServerId,
3224 cx: &mut AsyncAppContext,
3225 ) -> Result<Arc<LanguageServer>> {
3226 let workspace_config = adapter
3227 .adapter
3228 .clone()
3229 .workspace_configuration(&delegate, cx)
3230 .await?;
3231 let (language_server, mut initialization_options) = pending_server.task.await?;
3232
3233 let name = language_server.name();
3234 language_server
3235 .on_notification::<lsp::notification::PublishDiagnostics, _>({
3236 let adapter = adapter.clone();
3237 let this = project.clone();
3238 move |mut params, mut cx| {
3239 let adapter = adapter.clone();
3240 if let Some(this) = this.upgrade() {
3241 adapter.process_diagnostics(&mut params);
3242 this.update(&mut cx, |this, cx| {
3243 this.update_diagnostics(
3244 server_id,
3245 params,
3246 &adapter.disk_based_diagnostic_sources,
3247 cx,
3248 )
3249 .log_err();
3250 })
3251 .ok();
3252 }
3253 }
3254 })
3255 .detach();
3256
3257 language_server
3258 .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
3259 let adapter = adapter.adapter.clone();
3260 let delegate = delegate.clone();
3261 move |params, mut cx| {
3262 let adapter = adapter.clone();
3263 let delegate = delegate.clone();
3264 async move {
3265 let workspace_config =
3266 adapter.workspace_configuration(&delegate, &mut cx).await?;
3267 Ok(params
3268 .items
3269 .into_iter()
3270 .map(|item| {
3271 if let Some(section) = &item.section {
3272 workspace_config
3273 .get(section)
3274 .cloned()
3275 .unwrap_or(serde_json::Value::Null)
3276 } else {
3277 workspace_config.clone()
3278 }
3279 })
3280 .collect())
3281 }
3282 }
3283 })
3284 .detach();
3285
3286 // Even though we don't have handling for these requests, respond to them to
3287 // avoid stalling any language server like `gopls` which waits for a response
3288 // to these requests when initializing.
3289 language_server
3290 .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
3291 let this = project.clone();
3292 move |params, mut cx| {
3293 let this = this.clone();
3294 async move {
3295 this.update(&mut cx, |this, _| {
3296 if let Some(status) = this.language_server_statuses.get_mut(&server_id)
3297 {
3298 if let lsp::NumberOrString::String(token) = params.token {
3299 status.progress_tokens.insert(token);
3300 }
3301 }
3302 })?;
3303
3304 Ok(())
3305 }
3306 }
3307 })
3308 .detach();
3309
3310 language_server
3311 .on_request::<lsp::request::RegisterCapability, _, _>({
3312 let project = project.clone();
3313 move |params, mut cx| {
3314 let project = project.clone();
3315 async move {
3316 for reg in params.registrations {
3317 match reg.method.as_str() {
3318 "workspace/didChangeWatchedFiles" => {
3319 if let Some(options) = reg.register_options {
3320 let options = serde_json::from_value(options)?;
3321 project.update(&mut cx, |project, cx| {
3322 project.on_lsp_did_change_watched_files(
3323 server_id, ®.id, options, cx,
3324 );
3325 })?;
3326 }
3327 }
3328 "textDocument/rangeFormatting" => {
3329 project.update(&mut cx, |project, _| {
3330 if let Some(server) =
3331 project.language_server_for_id(server_id)
3332 {
3333 let options = reg
3334 .register_options
3335 .map(|options| {
3336 serde_json::from_value::<
3337 lsp::DocumentRangeFormattingOptions,
3338 >(
3339 options
3340 )
3341 })
3342 .transpose()?;
3343 let provider = match options {
3344 None => OneOf::Left(true),
3345 Some(options) => OneOf::Right(options),
3346 };
3347 server.update_capabilities(|capabilities| {
3348 capabilities.document_range_formatting_provider =
3349 Some(provider);
3350 })
3351 }
3352 anyhow::Ok(())
3353 })??;
3354 }
3355 "textDocument/onTypeFormatting" => {
3356 project.update(&mut cx, |project, _| {
3357 if let Some(server) =
3358 project.language_server_for_id(server_id)
3359 {
3360 let options = reg
3361 .register_options
3362 .map(|options| {
3363 serde_json::from_value::<
3364 lsp::DocumentOnTypeFormattingOptions,
3365 >(
3366 options
3367 )
3368 })
3369 .transpose()?;
3370 if let Some(options) = options {
3371 server.update_capabilities(|capabilities| {
3372 capabilities
3373 .document_on_type_formatting_provider =
3374 Some(options);
3375 })
3376 }
3377 }
3378 anyhow::Ok(())
3379 })??;
3380 }
3381 "textDocument/formatting" => {
3382 project.update(&mut cx, |project, _| {
3383 if let Some(server) =
3384 project.language_server_for_id(server_id)
3385 {
3386 let options = reg
3387 .register_options
3388 .map(|options| {
3389 serde_json::from_value::<
3390 lsp::DocumentFormattingOptions,
3391 >(
3392 options
3393 )
3394 })
3395 .transpose()?;
3396 let provider = match options {
3397 None => OneOf::Left(true),
3398 Some(options) => OneOf::Right(options),
3399 };
3400 server.update_capabilities(|capabilities| {
3401 capabilities.document_formatting_provider =
3402 Some(provider);
3403 })
3404 }
3405 anyhow::Ok(())
3406 })??;
3407 }
3408 _ => log::warn!("unhandled capability registration: {reg:?}"),
3409 }
3410 }
3411 Ok(())
3412 }
3413 }
3414 })
3415 .detach();
3416
3417 language_server
3418 .on_request::<lsp::request::UnregisterCapability, _, _>({
3419 let this = project.clone();
3420 move |params, mut cx| {
3421 let project = this.clone();
3422 async move {
3423 for unreg in params.unregisterations.iter() {
3424 match unreg.method.as_str() {
3425 "workspace/didChangeWatchedFiles" => {
3426 project.update(&mut cx, |project, cx| {
3427 project.on_lsp_unregister_did_change_watched_files(
3428 server_id, &unreg.id, cx,
3429 );
3430 })?;
3431 }
3432 "textDocument/rangeFormatting" => {
3433 project.update(&mut cx, |project, _| {
3434 if let Some(server) =
3435 project.language_server_for_id(server_id)
3436 {
3437 server.update_capabilities(|capabilities| {
3438 capabilities.document_range_formatting_provider =
3439 None
3440 })
3441 }
3442 })?;
3443 }
3444 "textDocument/onTypeFormatting" => {
3445 project.update(&mut cx, |project, _| {
3446 if let Some(server) =
3447 project.language_server_for_id(server_id)
3448 {
3449 server.update_capabilities(|capabilities| {
3450 capabilities.document_on_type_formatting_provider =
3451 None;
3452 })
3453 }
3454 })?;
3455 }
3456 "textDocument/formatting" => {
3457 project.update(&mut cx, |project, _| {
3458 if let Some(server) =
3459 project.language_server_for_id(server_id)
3460 {
3461 server.update_capabilities(|capabilities| {
3462 capabilities.document_formatting_provider = None;
3463 })
3464 }
3465 })?;
3466 }
3467 _ => log::warn!("unhandled capability unregistration: {unreg:?}"),
3468 }
3469 }
3470 Ok(())
3471 }
3472 }
3473 })
3474 .detach();
3475
3476 language_server
3477 .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
3478 let adapter = adapter.clone();
3479 let this = project.clone();
3480 move |params, cx| {
3481 Self::on_lsp_workspace_edit(
3482 this.clone(),
3483 params,
3484 server_id,
3485 adapter.clone(),
3486 cx,
3487 )
3488 }
3489 })
3490 .detach();
3491
3492 language_server
3493 .on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
3494 let this = project.clone();
3495 move |(), mut cx| {
3496 let this = this.clone();
3497 async move {
3498 this.update(&mut cx, |project, cx| {
3499 cx.emit(Event::RefreshInlayHints);
3500 project.remote_id().map(|project_id| {
3501 project.client.send(proto::RefreshInlayHints { project_id })
3502 })
3503 })?
3504 .transpose()?;
3505 Ok(())
3506 }
3507 }
3508 })
3509 .detach();
3510
3511 language_server
3512 .on_request::<lsp::request::ShowMessageRequest, _, _>({
3513 let this = project.clone();
3514 let name = name.to_string();
3515 move |params, mut cx| {
3516 let this = this.clone();
3517 let name = name.to_string();
3518 async move {
3519 let actions = params.actions.unwrap_or_default();
3520 let (tx, mut rx) = smol::channel::bounded(1);
3521 let request = LanguageServerPromptRequest {
3522 level: match params.typ {
3523 lsp::MessageType::ERROR => PromptLevel::Critical,
3524 lsp::MessageType::WARNING => PromptLevel::Warning,
3525 _ => PromptLevel::Info,
3526 },
3527 message: params.message,
3528 actions,
3529 response_channel: tx,
3530 lsp_name: name.clone(),
3531 };
3532
3533 if let Ok(_) = this.update(&mut cx, |_, cx| {
3534 cx.emit(Event::LanguageServerPrompt(request));
3535 }) {
3536 let response = rx.next().await;
3537
3538 Ok(response)
3539 } else {
3540 Ok(None)
3541 }
3542 }
3543 }
3544 })
3545 .detach();
3546
3547 let disk_based_diagnostics_progress_token =
3548 adapter.disk_based_diagnostics_progress_token.clone();
3549
3550 language_server
3551 .on_notification::<ServerStatus, _>({
3552 let this = project.clone();
3553 let name = name.to_string();
3554 move |params, mut cx| {
3555 let this = this.clone();
3556 let name = name.to_string();
3557 if let Some(ref message) = params.message {
3558 let message = message.trim();
3559 if !message.is_empty() {
3560 let formatted_message = format!(
3561 "Language server {name} (id {server_id}) status update: {message}"
3562 );
3563 match params.health {
3564 ServerHealthStatus::Ok => log::info!("{}", formatted_message),
3565 ServerHealthStatus::Warning => log::warn!("{}", formatted_message),
3566 ServerHealthStatus::Error => {
3567 log::error!("{}", formatted_message);
3568 let (tx, _rx) = smol::channel::bounded(1);
3569 let request = LanguageServerPromptRequest {
3570 level: PromptLevel::Critical,
3571 message: params.message.unwrap_or_default(),
3572 actions: Vec::new(),
3573 response_channel: tx,
3574 lsp_name: name.clone(),
3575 };
3576 let _ = this
3577 .update(&mut cx, |_, cx| {
3578 cx.emit(Event::LanguageServerPrompt(request));
3579 })
3580 .ok();
3581 }
3582 ServerHealthStatus::Other(status) => {
3583 log::info!(
3584 "Unknown server health: {status}\n{formatted_message}"
3585 )
3586 }
3587 }
3588 }
3589 }
3590 }
3591 })
3592 .detach();
3593 language_server
3594 .on_notification::<lsp::notification::ShowMessage, _>({
3595 let this = project.clone();
3596 let name = name.to_string();
3597 move |params, mut cx| {
3598 let this = this.clone();
3599 let name = name.to_string();
3600
3601 let (tx, _) = smol::channel::bounded(1);
3602 let request = LanguageServerPromptRequest {
3603 level: match params.typ {
3604 lsp::MessageType::ERROR => PromptLevel::Critical,
3605 lsp::MessageType::WARNING => PromptLevel::Warning,
3606 _ => PromptLevel::Info,
3607 },
3608 message: params.message,
3609 actions: vec![],
3610 response_channel: tx,
3611 lsp_name: name.clone(),
3612 };
3613
3614 let _ = this.update(&mut cx, |_, cx| {
3615 cx.emit(Event::LanguageServerPrompt(request));
3616 });
3617 }
3618 })
3619 .detach();
3620 language_server
3621 .on_notification::<lsp::notification::Progress, _>({
3622 let project = project.clone();
3623 move |params, mut cx| {
3624 if let Some(this) = project.upgrade() {
3625 this.update(&mut cx, |this, cx| {
3626 this.on_lsp_progress(
3627 params,
3628 server_id,
3629 disk_based_diagnostics_progress_token.clone(),
3630 cx,
3631 );
3632 })
3633 .ok();
3634 }
3635 }
3636 })
3637 .detach();
3638
3639 language_server
3640 .on_notification::<lsp::notification::LogMessage, _>({
3641 let project = project.clone();
3642 move |params, mut cx| {
3643 if let Some(this) = project.upgrade() {
3644 this.update(&mut cx, |_, cx| {
3645 cx.emit(Event::LanguageServerLog(
3646 server_id,
3647 LanguageServerLogType::Log(params.typ),
3648 params.message,
3649 ));
3650 })
3651 .ok();
3652 }
3653 }
3654 })
3655 .detach();
3656
3657 language_server
3658 .on_notification::<lsp::notification::LogTrace, _>({
3659 let project = project.clone();
3660 move |params, mut cx| {
3661 if let Some(this) = project.upgrade() {
3662 this.update(&mut cx, |_, cx| {
3663 cx.emit(Event::LanguageServerLog(
3664 server_id,
3665 LanguageServerLogType::Trace(params.verbose),
3666 params.message,
3667 ));
3668 })
3669 .ok();
3670 }
3671 }
3672 })
3673 .detach();
3674
3675 match (&mut initialization_options, override_options) {
3676 (Some(initialization_options), Some(override_options)) => {
3677 merge_json_value_into(override_options, initialization_options);
3678 }
3679 (None, override_options) => initialization_options = override_options,
3680 _ => {}
3681 }
3682
3683 let language_server = cx
3684 .update(|cx| language_server.initialize(initialization_options, cx))?
3685 .await
3686 .inspect_err(|_| {
3687 if let Some(this) = project.upgrade() {
3688 this.update(cx, |_, cx| cx.emit(Event::LanguageServerRemoved(server_id)))
3689 .ok();
3690 }
3691 })?;
3692
3693 language_server
3694 .notify::<lsp::notification::DidChangeConfiguration>(
3695 lsp::DidChangeConfigurationParams {
3696 settings: workspace_config,
3697 },
3698 )
3699 .ok();
3700
3701 Ok(language_server)
3702 }
3703
3704 fn insert_newly_running_language_server(
3705 &mut self,
3706 language: Arc<Language>,
3707 adapter: Arc<CachedLspAdapter>,
3708 language_server: Arc<LanguageServer>,
3709 server_id: LanguageServerId,
3710 key: (WorktreeId, LanguageServerName),
3711 cx: &mut ModelContext<Self>,
3712 ) -> Result<()> {
3713 // If the language server for this key doesn't match the server id, don't store the
3714 // server. Which will cause it to be dropped, killing the process
3715 if self
3716 .language_server_ids
3717 .get(&key)
3718 .map(|id| id != &server_id)
3719 .unwrap_or(false)
3720 {
3721 return Ok(());
3722 }
3723
3724 // Update language_servers collection with Running variant of LanguageServerState
3725 // indicating that the server is up and running and ready
3726 self.language_servers.insert(
3727 server_id,
3728 LanguageServerState::Running {
3729 adapter: adapter.clone(),
3730 language: language.clone(),
3731 server: language_server.clone(),
3732 simulate_disk_based_diagnostics_completion: None,
3733 },
3734 );
3735
3736 self.language_server_statuses.insert(
3737 server_id,
3738 LanguageServerStatus {
3739 name: language_server.name().to_string(),
3740 pending_work: Default::default(),
3741 has_pending_diagnostic_updates: false,
3742 progress_tokens: Default::default(),
3743 },
3744 );
3745
3746 cx.emit(Event::LanguageServerAdded(server_id));
3747
3748 if let Some(project_id) = self.remote_id() {
3749 self.client.send(proto::StartLanguageServer {
3750 project_id,
3751 server: Some(proto::LanguageServer {
3752 id: server_id.0 as u64,
3753 name: language_server.name().to_string(),
3754 }),
3755 })?;
3756 }
3757
3758 // Tell the language server about every open buffer in the worktree that matches the language.
3759 self.buffer_store.update(cx, |buffer_store, cx| {
3760 for buffer_handle in buffer_store.buffers() {
3761 let buffer = buffer_handle.read(cx);
3762 let file = match File::from_dyn(buffer.file()) {
3763 Some(file) => file,
3764 None => continue,
3765 };
3766 let language = match buffer.language() {
3767 Some(language) => language,
3768 None => continue,
3769 };
3770
3771 if file.worktree.read(cx).id() != key.0
3772 || !self
3773 .languages
3774 .lsp_adapters(&language)
3775 .iter()
3776 .any(|a| a.name == key.1)
3777 {
3778 continue;
3779 }
3780
3781 let file = match file.as_local() {
3782 Some(file) => file,
3783 None => continue,
3784 };
3785
3786 let versions = self
3787 .buffer_snapshots
3788 .entry(buffer.remote_id())
3789 .or_default()
3790 .entry(server_id)
3791 .or_insert_with(|| {
3792 vec![LspBufferSnapshot {
3793 version: 0,
3794 snapshot: buffer.text_snapshot(),
3795 }]
3796 });
3797
3798 let snapshot = versions.last().unwrap();
3799 let version = snapshot.version;
3800 let initial_snapshot = &snapshot.snapshot;
3801 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
3802 language_server.notify::<lsp::notification::DidOpenTextDocument>(
3803 lsp::DidOpenTextDocumentParams {
3804 text_document: lsp::TextDocumentItem::new(
3805 uri,
3806 adapter.language_id(&language),
3807 version,
3808 initial_snapshot.text(),
3809 ),
3810 },
3811 )?;
3812
3813 buffer_handle.update(cx, |buffer, cx| {
3814 buffer.set_completion_triggers(
3815 language_server
3816 .capabilities()
3817 .completion_provider
3818 .as_ref()
3819 .and_then(|provider| provider.trigger_characters.clone())
3820 .unwrap_or_default(),
3821 cx,
3822 )
3823 });
3824 }
3825 anyhow::Ok(())
3826 })?;
3827
3828 cx.notify();
3829 Ok(())
3830 }
3831
3832 // Returns a list of all of the worktrees which no longer have a language server and the root path
3833 // for the stopped server
3834 fn stop_language_server(
3835 &mut self,
3836 worktree_id: WorktreeId,
3837 adapter_name: LanguageServerName,
3838 cx: &mut ModelContext<Self>,
3839 ) -> Task<Vec<WorktreeId>> {
3840 let key = (worktree_id, adapter_name);
3841 if let Some(server_id) = self.language_server_ids.remove(&key) {
3842 let name = key.1 .0;
3843 log::info!("stopping language server {name}");
3844
3845 // Remove other entries for this language server as well
3846 let mut orphaned_worktrees = vec![worktree_id];
3847 let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
3848 for other_key in other_keys {
3849 if self.language_server_ids.get(&other_key) == Some(&server_id) {
3850 self.language_server_ids.remove(&other_key);
3851 orphaned_worktrees.push(other_key.0);
3852 }
3853 }
3854
3855 self.buffer_store.update(cx, |buffer_store, cx| {
3856 for buffer in buffer_store.buffers() {
3857 buffer.update(cx, |buffer, cx| {
3858 buffer.update_diagnostics(server_id, Default::default(), cx);
3859 });
3860 }
3861 });
3862
3863 let project_id = self.remote_id();
3864 for (worktree_id, summaries) in self.diagnostic_summaries.iter_mut() {
3865 summaries.retain(|path, summaries_by_server_id| {
3866 if summaries_by_server_id.remove(&server_id).is_some() {
3867 if let Some(project_id) = project_id {
3868 self.client
3869 .send(proto::UpdateDiagnosticSummary {
3870 project_id,
3871 worktree_id: worktree_id.to_proto(),
3872 summary: Some(proto::DiagnosticSummary {
3873 path: path.to_string_lossy().to_string(),
3874 language_server_id: server_id.0 as u64,
3875 error_count: 0,
3876 warning_count: 0,
3877 }),
3878 })
3879 .log_err();
3880 }
3881 !summaries_by_server_id.is_empty()
3882 } else {
3883 true
3884 }
3885 });
3886 }
3887
3888 for diagnostics in self.diagnostics.values_mut() {
3889 diagnostics.retain(|_, diagnostics_by_server_id| {
3890 if let Ok(ix) =
3891 diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0)
3892 {
3893 diagnostics_by_server_id.remove(ix);
3894 !diagnostics_by_server_id.is_empty()
3895 } else {
3896 true
3897 }
3898 });
3899 }
3900
3901 self.language_server_watched_paths.remove(&server_id);
3902 self.language_server_statuses.remove(&server_id);
3903 cx.notify();
3904
3905 let server_state = self.language_servers.remove(&server_id);
3906 cx.emit(Event::LanguageServerRemoved(server_id));
3907 cx.spawn(move |_, cx| async move {
3908 Self::shutdown_language_server(server_state, name, cx).await;
3909 orphaned_worktrees
3910 })
3911 } else {
3912 Task::ready(Vec::new())
3913 }
3914 }
3915
3916 async fn shutdown_language_server(
3917 server_state: Option<LanguageServerState>,
3918 name: Arc<str>,
3919 cx: AsyncAppContext,
3920 ) {
3921 let server = match server_state {
3922 Some(LanguageServerState::Starting(task)) => {
3923 let mut timer = cx
3924 .background_executor()
3925 .timer(SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT)
3926 .fuse();
3927
3928 select! {
3929 server = task.fuse() => server,
3930 _ = timer => {
3931 log::info!(
3932 "timeout waiting for language server {} to finish launching before stopping",
3933 name
3934 );
3935 None
3936 },
3937 }
3938 }
3939
3940 Some(LanguageServerState::Running { server, .. }) => Some(server),
3941
3942 None => None,
3943 };
3944
3945 if let Some(server) = server {
3946 if let Some(shutdown) = server.shutdown() {
3947 shutdown.await;
3948 }
3949 }
3950 }
3951
3952 async fn handle_restart_language_servers(
3953 project: Model<Self>,
3954 envelope: TypedEnvelope<proto::RestartLanguageServers>,
3955 mut cx: AsyncAppContext,
3956 ) -> Result<proto::Ack> {
3957 project.update(&mut cx, |project, cx| {
3958 let buffers: Vec<_> = envelope
3959 .payload
3960 .buffer_ids
3961 .into_iter()
3962 .flat_map(|buffer_id| {
3963 project.buffer_for_id(BufferId::new(buffer_id).log_err()?, cx)
3964 })
3965 .collect();
3966 project.restart_language_servers_for_buffers(buffers, cx)
3967 })?;
3968
3969 Ok(proto::Ack {})
3970 }
3971
3972 pub fn restart_language_servers_for_buffers(
3973 &mut self,
3974 buffers: impl IntoIterator<Item = Model<Buffer>>,
3975 cx: &mut ModelContext<Self>,
3976 ) {
3977 if self.is_via_collab() {
3978 let request = self.client.request(proto::RestartLanguageServers {
3979 project_id: self.remote_id().unwrap(),
3980 buffer_ids: buffers
3981 .into_iter()
3982 .map(|b| b.read(cx).remote_id().to_proto())
3983 .collect(),
3984 });
3985 cx.background_executor()
3986 .spawn(request)
3987 .detach_and_log_err(cx);
3988 return;
3989 }
3990
3991 #[allow(clippy::mutable_key_type)]
3992 let language_server_lookup_info: HashSet<(Model<Worktree>, Arc<Language>)> = buffers
3993 .into_iter()
3994 .filter_map(|buffer| {
3995 let buffer = buffer.read(cx);
3996 let file = buffer.file()?;
3997 let worktree = File::from_dyn(Some(file))?.worktree.clone();
3998 let language = self
3999 .languages
4000 .language_for_file(file, Some(buffer.as_rope()), cx)
4001 .now_or_never()?
4002 .ok()?;
4003 Some((worktree, language))
4004 })
4005 .collect();
4006 for (worktree, language) in language_server_lookup_info {
4007 self.restart_language_servers(worktree, language, cx);
4008 }
4009 }
4010
4011 fn restart_language_servers(
4012 &mut self,
4013 worktree: Model<Worktree>,
4014 language: Arc<Language>,
4015 cx: &mut ModelContext<Self>,
4016 ) {
4017 let worktree_id = worktree.read(cx).id();
4018
4019 let stop_tasks = self
4020 .languages
4021 .clone()
4022 .lsp_adapters(&language)
4023 .iter()
4024 .map(|adapter| {
4025 let stop_task = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
4026 (stop_task, adapter.name.clone())
4027 })
4028 .collect::<Vec<_>>();
4029 if stop_tasks.is_empty() {
4030 return;
4031 }
4032
4033 cx.spawn(move |this, mut cx| async move {
4034 // For each stopped language server, record all of the worktrees with which
4035 // it was associated.
4036 let mut affected_worktrees = Vec::new();
4037 for (stop_task, language_server_name) in stop_tasks {
4038 for affected_worktree_id in stop_task.await {
4039 affected_worktrees.push((affected_worktree_id, language_server_name.clone()));
4040 }
4041 }
4042
4043 this.update(&mut cx, |this, cx| {
4044 // Restart the language server for the given worktree.
4045 this.start_language_servers(&worktree, language.clone(), cx);
4046
4047 // Lookup new server ids and set them for each of the orphaned worktrees
4048 for (affected_worktree_id, language_server_name) in affected_worktrees {
4049 if let Some(new_server_id) = this
4050 .language_server_ids
4051 .get(&(worktree_id, language_server_name.clone()))
4052 .cloned()
4053 {
4054 this.language_server_ids
4055 .insert((affected_worktree_id, language_server_name), new_server_id);
4056 }
4057 }
4058 })
4059 .ok();
4060 })
4061 .detach();
4062 }
4063
4064 pub fn cancel_language_server_work_for_buffers(
4065 &mut self,
4066 buffers: impl IntoIterator<Item = Model<Buffer>>,
4067 cx: &mut ModelContext<Self>,
4068 ) {
4069 let servers = buffers
4070 .into_iter()
4071 .flat_map(|buffer| {
4072 self.language_server_ids_for_buffer(buffer.read(cx), cx)
4073 .into_iter()
4074 })
4075 .collect::<HashSet<_>>();
4076
4077 for server_id in servers {
4078 self.cancel_language_server_work(server_id, None, cx);
4079 }
4080 }
4081
4082 pub fn cancel_language_server_work(
4083 &mut self,
4084 server_id: LanguageServerId,
4085 token_to_cancel: Option<String>,
4086 _cx: &mut ModelContext<Self>,
4087 ) {
4088 let status = self.language_server_statuses.get(&server_id);
4089 let server = self.language_servers.get(&server_id);
4090 if let Some((server, status)) = server.zip(status) {
4091 if let LanguageServerState::Running { server, .. } = server {
4092 for (token, progress) in &status.pending_work {
4093 if let Some(token_to_cancel) = token_to_cancel.as_ref() {
4094 if token != token_to_cancel {
4095 continue;
4096 }
4097 }
4098 if progress.is_cancellable {
4099 server
4100 .notify::<lsp::notification::WorkDoneProgressCancel>(
4101 WorkDoneProgressCancelParams {
4102 token: lsp::NumberOrString::String(token.clone()),
4103 },
4104 )
4105 .ok();
4106 }
4107 }
4108 }
4109 }
4110 }
4111
4112 fn check_errored_server(
4113 language: Arc<Language>,
4114 adapter: Arc<CachedLspAdapter>,
4115 server_id: LanguageServerId,
4116 installation_test_binary: Option<LanguageServerBinary>,
4117 cx: &mut ModelContext<Self>,
4118 ) {
4119 if !adapter.can_be_reinstalled() {
4120 log::info!(
4121 "Validation check requested for {:?} but it cannot be reinstalled",
4122 adapter.name.0
4123 );
4124 return;
4125 }
4126
4127 cx.spawn(move |this, mut cx| async move {
4128 log::info!("About to spawn test binary");
4129
4130 // A lack of test binary counts as a failure
4131 let process = installation_test_binary.and_then(|binary| {
4132 smol::process::Command::new(&binary.path)
4133 .current_dir(&binary.path)
4134 .args(binary.arguments)
4135 .stdin(Stdio::piped())
4136 .stdout(Stdio::piped())
4137 .stderr(Stdio::inherit())
4138 .kill_on_drop(true)
4139 .spawn()
4140 .ok()
4141 });
4142
4143 const PROCESS_TIMEOUT: Duration = Duration::from_secs(5);
4144 let mut timeout = cx.background_executor().timer(PROCESS_TIMEOUT).fuse();
4145
4146 let mut errored = false;
4147 if let Some(mut process) = process {
4148 futures::select! {
4149 status = process.status().fuse() => match status {
4150 Ok(status) => errored = !status.success(),
4151 Err(_) => errored = true,
4152 },
4153
4154 _ = timeout => {
4155 log::info!("test binary time-ed out, this counts as a success");
4156 _ = process.kill();
4157 }
4158 }
4159 } else {
4160 log::warn!("test binary failed to launch");
4161 errored = true;
4162 }
4163
4164 if errored {
4165 log::warn!("test binary check failed");
4166 let task = this
4167 .update(&mut cx, move |this, cx| {
4168 this.reinstall_language_server(language, adapter, server_id, cx)
4169 })
4170 .ok()
4171 .flatten();
4172
4173 if let Some(task) = task {
4174 task.await;
4175 }
4176 }
4177 })
4178 .detach();
4179 }
4180
4181 fn enqueue_buffer_ordered_message(&mut self, message: BufferOrderedMessage) -> Result<()> {
4182 self.buffer_ordered_messages_tx
4183 .unbounded_send(message)
4184 .map_err(|e| anyhow!(e))
4185 }
4186
4187 fn on_lsp_progress(
4188 &mut self,
4189 progress: lsp::ProgressParams,
4190 language_server_id: LanguageServerId,
4191 disk_based_diagnostics_progress_token: Option<String>,
4192 cx: &mut ModelContext<Self>,
4193 ) {
4194 let token = match progress.token {
4195 lsp::NumberOrString::String(token) => token,
4196 lsp::NumberOrString::Number(token) => {
4197 log::info!("skipping numeric progress token {}", token);
4198 return;
4199 }
4200 };
4201
4202 let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
4203 let language_server_status =
4204 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
4205 status
4206 } else {
4207 return;
4208 };
4209
4210 if !language_server_status.progress_tokens.contains(&token) {
4211 return;
4212 }
4213
4214 let is_disk_based_diagnostics_progress = disk_based_diagnostics_progress_token
4215 .as_ref()
4216 .map_or(false, |disk_based_token| {
4217 token.starts_with(disk_based_token)
4218 });
4219
4220 match progress {
4221 lsp::WorkDoneProgress::Begin(report) => {
4222 if is_disk_based_diagnostics_progress {
4223 self.disk_based_diagnostics_started(language_server_id, cx);
4224 }
4225 self.on_lsp_work_start(
4226 language_server_id,
4227 token.clone(),
4228 LanguageServerProgress {
4229 title: Some(report.title),
4230 is_disk_based_diagnostics_progress,
4231 is_cancellable: report.cancellable.unwrap_or(false),
4232 message: report.message.clone(),
4233 percentage: report.percentage.map(|p| p as usize),
4234 last_update_at: cx.background_executor().now(),
4235 },
4236 cx,
4237 );
4238 }
4239 lsp::WorkDoneProgress::Report(report) => {
4240 if self.on_lsp_work_progress(
4241 language_server_id,
4242 token.clone(),
4243 LanguageServerProgress {
4244 title: None,
4245 is_disk_based_diagnostics_progress,
4246 is_cancellable: report.cancellable.unwrap_or(false),
4247 message: report.message.clone(),
4248 percentage: report.percentage.map(|p| p as usize),
4249 last_update_at: cx.background_executor().now(),
4250 },
4251 cx,
4252 ) {
4253 self.enqueue_buffer_ordered_message(
4254 BufferOrderedMessage::LanguageServerUpdate {
4255 language_server_id,
4256 message: proto::update_language_server::Variant::WorkProgress(
4257 proto::LspWorkProgress {
4258 token,
4259 message: report.message,
4260 percentage: report.percentage,
4261 },
4262 ),
4263 },
4264 )
4265 .ok();
4266 }
4267 }
4268 lsp::WorkDoneProgress::End(_) => {
4269 language_server_status.progress_tokens.remove(&token);
4270 self.on_lsp_work_end(language_server_id, token.clone(), cx);
4271 if is_disk_based_diagnostics_progress {
4272 self.disk_based_diagnostics_finished(language_server_id, cx);
4273 }
4274 }
4275 }
4276 }
4277
4278 fn on_lsp_work_start(
4279 &mut self,
4280 language_server_id: LanguageServerId,
4281 token: String,
4282 progress: LanguageServerProgress,
4283 cx: &mut ModelContext<Self>,
4284 ) {
4285 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
4286 status.pending_work.insert(token.clone(), progress.clone());
4287 cx.notify();
4288 }
4289
4290 if self.is_local_or_ssh() {
4291 self.enqueue_buffer_ordered_message(BufferOrderedMessage::LanguageServerUpdate {
4292 language_server_id,
4293 message: proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
4294 token,
4295 title: progress.title,
4296 message: progress.message,
4297 percentage: progress.percentage.map(|p| p as u32),
4298 }),
4299 })
4300 .ok();
4301 }
4302 }
4303
4304 fn on_lsp_work_progress(
4305 &mut self,
4306 language_server_id: LanguageServerId,
4307 token: String,
4308 progress: LanguageServerProgress,
4309 cx: &mut ModelContext<Self>,
4310 ) -> bool {
4311 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
4312 match status.pending_work.entry(token) {
4313 btree_map::Entry::Vacant(entry) => {
4314 entry.insert(progress);
4315 cx.notify();
4316 return true;
4317 }
4318 btree_map::Entry::Occupied(mut entry) => {
4319 let entry = entry.get_mut();
4320 if (progress.last_update_at - entry.last_update_at)
4321 >= SERVER_PROGRESS_THROTTLE_TIMEOUT
4322 {
4323 entry.last_update_at = progress.last_update_at;
4324 if progress.message.is_some() {
4325 entry.message = progress.message;
4326 }
4327 if progress.percentage.is_some() {
4328 entry.percentage = progress.percentage;
4329 }
4330 cx.notify();
4331 return true;
4332 }
4333 }
4334 }
4335 }
4336
4337 false
4338 }
4339
4340 fn on_lsp_work_end(
4341 &mut self,
4342 language_server_id: LanguageServerId,
4343 token: String,
4344 cx: &mut ModelContext<Self>,
4345 ) {
4346 if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
4347 if let Some(work) = status.pending_work.remove(&token) {
4348 if !work.is_disk_based_diagnostics_progress {
4349 cx.emit(Event::RefreshInlayHints);
4350 }
4351 }
4352 cx.notify();
4353 }
4354
4355 if self.is_local_or_ssh() {
4356 self.enqueue_buffer_ordered_message(BufferOrderedMessage::LanguageServerUpdate {
4357 language_server_id,
4358 message: proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd {
4359 token,
4360 }),
4361 })
4362 .ok();
4363 }
4364 }
4365
4366 fn on_lsp_did_change_watched_files(
4367 &mut self,
4368 language_server_id: LanguageServerId,
4369 registration_id: &str,
4370 params: DidChangeWatchedFilesRegistrationOptions,
4371 cx: &mut ModelContext<Self>,
4372 ) {
4373 let registrations = self
4374 .language_server_watcher_registrations
4375 .entry(language_server_id)
4376 .or_default();
4377
4378 registrations.insert(registration_id.to_string(), params.watchers);
4379
4380 self.rebuild_watched_paths(language_server_id, cx);
4381 }
4382
4383 fn on_lsp_unregister_did_change_watched_files(
4384 &mut self,
4385 language_server_id: LanguageServerId,
4386 registration_id: &str,
4387 cx: &mut ModelContext<Self>,
4388 ) {
4389 let registrations = self
4390 .language_server_watcher_registrations
4391 .entry(language_server_id)
4392 .or_default();
4393
4394 if registrations.remove(registration_id).is_some() {
4395 log::info!(
4396 "language server {}: unregistered workspace/DidChangeWatchedFiles capability with id {}",
4397 language_server_id,
4398 registration_id
4399 );
4400 } else {
4401 log::warn!(
4402 "language server {}: failed to unregister workspace/DidChangeWatchedFiles capability with id {}. not registered.",
4403 language_server_id,
4404 registration_id
4405 );
4406 }
4407
4408 self.rebuild_watched_paths(language_server_id, cx);
4409 }
4410
4411 fn rebuild_watched_paths(
4412 &mut self,
4413 language_server_id: LanguageServerId,
4414 cx: &mut ModelContext<Self>,
4415 ) {
4416 let Some(watchers) = self
4417 .language_server_watcher_registrations
4418 .get(&language_server_id)
4419 else {
4420 return;
4421 };
4422
4423 let watched_paths = self
4424 .language_server_watched_paths
4425 .entry(language_server_id)
4426 .or_default();
4427
4428 let mut builders = HashMap::default();
4429 for watcher in watchers.values().flatten() {
4430 for worktree in self.worktree_store.read(cx).worktrees().collect::<Vec<_>>() {
4431 let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
4432 if let Some(abs_path) = tree.abs_path().to_str() {
4433 let relative_glob_pattern = match &watcher.glob_pattern {
4434 lsp::GlobPattern::String(s) => Some(
4435 s.strip_prefix(abs_path)
4436 .unwrap_or(s)
4437 .strip_prefix(std::path::MAIN_SEPARATOR)
4438 .unwrap_or(s),
4439 ),
4440 lsp::GlobPattern::Relative(rp) => {
4441 let base_uri = match &rp.base_uri {
4442 lsp::OneOf::Left(workspace_folder) => &workspace_folder.uri,
4443 lsp::OneOf::Right(base_uri) => base_uri,
4444 };
4445 base_uri.to_file_path().ok().and_then(|file_path| {
4446 (file_path.to_str() == Some(abs_path))
4447 .then_some(rp.pattern.as_str())
4448 })
4449 }
4450 };
4451 if let Some(relative_glob_pattern) = relative_glob_pattern {
4452 let literal_prefix = glob_literal_prefix(relative_glob_pattern);
4453 tree.as_local_mut()
4454 .unwrap()
4455 .add_path_prefix_to_scan(Path::new(literal_prefix).into());
4456 if let Some(glob) = Glob::new(relative_glob_pattern).log_err() {
4457 builders
4458 .entry(tree.id())
4459 .or_insert_with(|| GlobSetBuilder::new())
4460 .add(glob);
4461 }
4462 return true;
4463 }
4464 }
4465 false
4466 });
4467 if glob_is_inside_worktree {
4468 break;
4469 }
4470 }
4471 }
4472
4473 watched_paths.clear();
4474 for (worktree_id, builder) in builders {
4475 if let Ok(globset) = builder.build() {
4476 watched_paths.insert(worktree_id, globset);
4477 }
4478 }
4479
4480 cx.notify();
4481 }
4482
4483 async fn on_lsp_workspace_edit(
4484 this: WeakModel<Self>,
4485 params: lsp::ApplyWorkspaceEditParams,
4486 server_id: LanguageServerId,
4487 adapter: Arc<CachedLspAdapter>,
4488 mut cx: AsyncAppContext,
4489 ) -> Result<lsp::ApplyWorkspaceEditResponse> {
4490 let this = this
4491 .upgrade()
4492 .ok_or_else(|| anyhow!("project project closed"))?;
4493 let language_server = this
4494 .update(&mut cx, |this, _| this.language_server_for_id(server_id))?
4495 .ok_or_else(|| anyhow!("language server not found"))?;
4496 let transaction = Self::deserialize_workspace_edit(
4497 this.clone(),
4498 params.edit,
4499 true,
4500 adapter.clone(),
4501 language_server.clone(),
4502 &mut cx,
4503 )
4504 .await
4505 .log_err();
4506 this.update(&mut cx, |this, _| {
4507 if let Some(transaction) = transaction {
4508 this.last_workspace_edits_by_language_server
4509 .insert(server_id, transaction);
4510 }
4511 })?;
4512 Ok(lsp::ApplyWorkspaceEditResponse {
4513 applied: true,
4514 failed_change: None,
4515 failure_reason: None,
4516 })
4517 }
4518
4519 pub fn language_server_statuses(
4520 &self,
4521 ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &LanguageServerStatus)> {
4522 self.language_server_statuses
4523 .iter()
4524 .map(|(key, value)| (*key, value))
4525 }
4526
4527 pub fn last_formatting_failure(&self) -> Option<&str> {
4528 self.last_formatting_failure.as_deref()
4529 }
4530
4531 pub fn update_diagnostics(
4532 &mut self,
4533 language_server_id: LanguageServerId,
4534 mut params: lsp::PublishDiagnosticsParams,
4535 disk_based_sources: &[String],
4536 cx: &mut ModelContext<Self>,
4537 ) -> Result<()> {
4538 let abs_path = params
4539 .uri
4540 .to_file_path()
4541 .map_err(|_| anyhow!("URI is not a file"))?;
4542 let mut diagnostics = Vec::default();
4543 let mut primary_diagnostic_group_ids = HashMap::default();
4544 let mut sources_by_group_id = HashMap::default();
4545 let mut supporting_diagnostics = HashMap::default();
4546
4547 // Ensure that primary diagnostics are always the most severe
4548 params.diagnostics.sort_by_key(|item| item.severity);
4549
4550 for diagnostic in ¶ms.diagnostics {
4551 let source = diagnostic.source.as_ref();
4552 let code = diagnostic.code.as_ref().map(|code| match code {
4553 lsp::NumberOrString::Number(code) => code.to_string(),
4554 lsp::NumberOrString::String(code) => code.clone(),
4555 });
4556 let range = range_from_lsp(diagnostic.range);
4557 let is_supporting = diagnostic
4558 .related_information
4559 .as_ref()
4560 .map_or(false, |infos| {
4561 infos.iter().any(|info| {
4562 primary_diagnostic_group_ids.contains_key(&(
4563 source,
4564 code.clone(),
4565 range_from_lsp(info.location.range),
4566 ))
4567 })
4568 });
4569
4570 let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
4571 tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
4572 });
4573
4574 if is_supporting {
4575 supporting_diagnostics.insert(
4576 (source, code.clone(), range),
4577 (diagnostic.severity, is_unnecessary),
4578 );
4579 } else {
4580 let group_id = post_inc(&mut self.next_diagnostic_group_id);
4581 let is_disk_based =
4582 source.map_or(false, |source| disk_based_sources.contains(source));
4583
4584 sources_by_group_id.insert(group_id, source);
4585 primary_diagnostic_group_ids
4586 .insert((source, code.clone(), range.clone()), group_id);
4587
4588 diagnostics.push(DiagnosticEntry {
4589 range,
4590 diagnostic: Diagnostic {
4591 source: diagnostic.source.clone(),
4592 code: code.clone(),
4593 severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
4594 message: diagnostic.message.trim().to_string(),
4595 group_id,
4596 is_primary: true,
4597 is_disk_based,
4598 is_unnecessary,
4599 data: diagnostic.data.clone(),
4600 },
4601 });
4602 if let Some(infos) = &diagnostic.related_information {
4603 for info in infos {
4604 if info.location.uri == params.uri && !info.message.is_empty() {
4605 let range = range_from_lsp(info.location.range);
4606 diagnostics.push(DiagnosticEntry {
4607 range,
4608 diagnostic: Diagnostic {
4609 source: diagnostic.source.clone(),
4610 code: code.clone(),
4611 severity: DiagnosticSeverity::INFORMATION,
4612 message: info.message.trim().to_string(),
4613 group_id,
4614 is_primary: false,
4615 is_disk_based,
4616 is_unnecessary: false,
4617 data: diagnostic.data.clone(),
4618 },
4619 });
4620 }
4621 }
4622 }
4623 }
4624 }
4625
4626 for entry in &mut diagnostics {
4627 let diagnostic = &mut entry.diagnostic;
4628 if !diagnostic.is_primary {
4629 let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
4630 if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
4631 source,
4632 diagnostic.code.clone(),
4633 entry.range.clone(),
4634 )) {
4635 if let Some(severity) = severity {
4636 diagnostic.severity = severity;
4637 }
4638 diagnostic.is_unnecessary = is_unnecessary;
4639 }
4640 }
4641 }
4642
4643 self.update_diagnostic_entries(
4644 language_server_id,
4645 abs_path,
4646 params.version,
4647 diagnostics,
4648 cx,
4649 )?;
4650 Ok(())
4651 }
4652
4653 pub fn update_diagnostic_entries(
4654 &mut self,
4655 server_id: LanguageServerId,
4656 abs_path: PathBuf,
4657 version: Option<i32>,
4658 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
4659 cx: &mut ModelContext<Project>,
4660 ) -> Result<(), anyhow::Error> {
4661 let (worktree, relative_path) = self
4662 .find_worktree(&abs_path, cx)
4663 .ok_or_else(|| anyhow!("no worktree found for diagnostics path {abs_path:?}"))?;
4664
4665 let project_path = ProjectPath {
4666 worktree_id: worktree.read(cx).id(),
4667 path: relative_path.into(),
4668 };
4669
4670 if let Some(buffer) = self.get_open_buffer(&project_path, cx) {
4671 self.update_buffer_diagnostics(&buffer, server_id, version, diagnostics.clone(), cx)?;
4672 }
4673
4674 let updated = worktree.update(cx, |worktree, cx| {
4675 self.update_worktree_diagnostics(
4676 worktree.id(),
4677 server_id,
4678 project_path.path.clone(),
4679 diagnostics,
4680 cx,
4681 )
4682 })?;
4683 if updated {
4684 cx.emit(Event::DiagnosticsUpdated {
4685 language_server_id: server_id,
4686 path: project_path,
4687 });
4688 }
4689 Ok(())
4690 }
4691
4692 pub fn update_worktree_diagnostics(
4693 &mut self,
4694 worktree_id: WorktreeId,
4695 server_id: LanguageServerId,
4696 worktree_path: Arc<Path>,
4697 diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
4698 _: &mut ModelContext<Worktree>,
4699 ) -> Result<bool> {
4700 let summaries_for_tree = self.diagnostic_summaries.entry(worktree_id).or_default();
4701 let diagnostics_for_tree = self.diagnostics.entry(worktree_id).or_default();
4702 let summaries_by_server_id = summaries_for_tree.entry(worktree_path.clone()).or_default();
4703
4704 let old_summary = summaries_by_server_id
4705 .remove(&server_id)
4706 .unwrap_or_default();
4707
4708 let new_summary = DiagnosticSummary::new(&diagnostics);
4709 if new_summary.is_empty() {
4710 if let Some(diagnostics_by_server_id) = diagnostics_for_tree.get_mut(&worktree_path) {
4711 if let Ok(ix) = diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
4712 diagnostics_by_server_id.remove(ix);
4713 }
4714 if diagnostics_by_server_id.is_empty() {
4715 diagnostics_for_tree.remove(&worktree_path);
4716 }
4717 }
4718 } else {
4719 summaries_by_server_id.insert(server_id, new_summary);
4720 let diagnostics_by_server_id = diagnostics_for_tree
4721 .entry(worktree_path.clone())
4722 .or_default();
4723 match diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
4724 Ok(ix) => {
4725 diagnostics_by_server_id[ix] = (server_id, diagnostics);
4726 }
4727 Err(ix) => {
4728 diagnostics_by_server_id.insert(ix, (server_id, diagnostics));
4729 }
4730 }
4731 }
4732
4733 if !old_summary.is_empty() || !new_summary.is_empty() {
4734 if let Some(project_id) = self.remote_id() {
4735 self.client
4736 .send(proto::UpdateDiagnosticSummary {
4737 project_id,
4738 worktree_id: worktree_id.to_proto(),
4739 summary: Some(proto::DiagnosticSummary {
4740 path: worktree_path.to_string_lossy().to_string(),
4741 language_server_id: server_id.0 as u64,
4742 error_count: new_summary.error_count as u32,
4743 warning_count: new_summary.warning_count as u32,
4744 }),
4745 })
4746 .log_err();
4747 }
4748 }
4749
4750 Ok(!old_summary.is_empty() || !new_summary.is_empty())
4751 }
4752
4753 fn update_buffer_diagnostics(
4754 &mut self,
4755 buffer: &Model<Buffer>,
4756 server_id: LanguageServerId,
4757 version: Option<i32>,
4758 mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
4759 cx: &mut ModelContext<Self>,
4760 ) -> Result<()> {
4761 fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
4762 Ordering::Equal
4763 .then_with(|| b.is_primary.cmp(&a.is_primary))
4764 .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
4765 .then_with(|| a.severity.cmp(&b.severity))
4766 .then_with(|| a.message.cmp(&b.message))
4767 }
4768
4769 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx)?;
4770
4771 diagnostics.sort_unstable_by(|a, b| {
4772 Ordering::Equal
4773 .then_with(|| a.range.start.cmp(&b.range.start))
4774 .then_with(|| b.range.end.cmp(&a.range.end))
4775 .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
4776 });
4777
4778 let mut sanitized_diagnostics = Vec::new();
4779 let edits_since_save = Patch::new(
4780 snapshot
4781 .edits_since::<Unclipped<PointUtf16>>(buffer.read(cx).saved_version())
4782 .collect(),
4783 );
4784 for entry in diagnostics {
4785 let start;
4786 let end;
4787 if entry.diagnostic.is_disk_based {
4788 // Some diagnostics are based on files on disk instead of buffers'
4789 // current contents. Adjust these diagnostics' ranges to reflect
4790 // any unsaved edits.
4791 start = edits_since_save.old_to_new(entry.range.start);
4792 end = edits_since_save.old_to_new(entry.range.end);
4793 } else {
4794 start = entry.range.start;
4795 end = entry.range.end;
4796 }
4797
4798 let mut range = snapshot.clip_point_utf16(start, Bias::Left)
4799 ..snapshot.clip_point_utf16(end, Bias::Right);
4800
4801 // Expand empty ranges by one codepoint
4802 if range.start == range.end {
4803 // This will be go to the next boundary when being clipped
4804 range.end.column += 1;
4805 range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right);
4806 if range.start == range.end && range.end.column > 0 {
4807 range.start.column -= 1;
4808 range.start = snapshot.clip_point_utf16(Unclipped(range.start), Bias::Left);
4809 }
4810 }
4811
4812 sanitized_diagnostics.push(DiagnosticEntry {
4813 range,
4814 diagnostic: entry.diagnostic,
4815 });
4816 }
4817 drop(edits_since_save);
4818
4819 let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
4820 buffer.update(cx, |buffer, cx| {
4821 buffer.update_diagnostics(server_id, set, cx)
4822 });
4823 Ok(())
4824 }
4825
4826 pub fn reload_buffers(
4827 &self,
4828 buffers: HashSet<Model<Buffer>>,
4829 push_to_history: bool,
4830 cx: &mut ModelContext<Self>,
4831 ) -> Task<Result<ProjectTransaction>> {
4832 let mut local_buffers = Vec::new();
4833 let mut remote_buffers = None;
4834 for buffer_handle in buffers {
4835 let buffer = buffer_handle.read(cx);
4836 if buffer.is_dirty() {
4837 if let Some(file) = File::from_dyn(buffer.file()) {
4838 if file.is_local() {
4839 local_buffers.push(buffer_handle);
4840 } else {
4841 remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
4842 }
4843 }
4844 }
4845 }
4846
4847 let remote_buffers = self.remote_id().zip(remote_buffers);
4848 let client = self.client.clone();
4849
4850 cx.spawn(move |this, mut cx| async move {
4851 let mut project_transaction = ProjectTransaction::default();
4852
4853 if let Some((project_id, remote_buffers)) = remote_buffers {
4854 let response = client
4855 .request(proto::ReloadBuffers {
4856 project_id,
4857 buffer_ids: remote_buffers
4858 .iter()
4859 .filter_map(|buffer| {
4860 buffer
4861 .update(&mut cx, |buffer, _| buffer.remote_id().into())
4862 .ok()
4863 })
4864 .collect(),
4865 })
4866 .await?
4867 .transaction
4868 .ok_or_else(|| anyhow!("missing transaction"))?;
4869 Self::deserialize_project_transaction(this, response, push_to_history, cx.clone())
4870 .await?;
4871 }
4872
4873 for buffer in local_buffers {
4874 let transaction = buffer
4875 .update(&mut cx, |buffer, cx| buffer.reload(cx))?
4876 .await?;
4877 buffer.update(&mut cx, |buffer, cx| {
4878 if let Some(transaction) = transaction {
4879 if !push_to_history {
4880 buffer.forget_transaction(transaction.id);
4881 }
4882 project_transaction.0.insert(cx.handle(), transaction);
4883 }
4884 })?;
4885 }
4886
4887 Ok(project_transaction)
4888 })
4889 }
4890
4891 pub fn format(
4892 &mut self,
4893 buffers: HashSet<Model<Buffer>>,
4894 push_to_history: bool,
4895 trigger: FormatTrigger,
4896 cx: &mut ModelContext<Project>,
4897 ) -> Task<anyhow::Result<ProjectTransaction>> {
4898 if self.is_local_or_ssh() {
4899 let buffers_with_paths = buffers
4900 .into_iter()
4901 .map(|buffer_handle| {
4902 let buffer = buffer_handle.read(cx);
4903 let buffer_abs_path = File::from_dyn(buffer.file())
4904 .and_then(|file| file.as_local().map(|f| f.abs_path(cx)));
4905 (buffer_handle, buffer_abs_path)
4906 })
4907 .collect::<Vec<_>>();
4908
4909 cx.spawn(move |project, mut cx| async move {
4910 let result = Self::format_locally(
4911 project.clone(),
4912 buffers_with_paths,
4913 push_to_history,
4914 trigger,
4915 cx.clone(),
4916 )
4917 .await;
4918
4919 project.update(&mut cx, |project, _| match &result {
4920 Ok(_) => project.last_formatting_failure = None,
4921 Err(error) => {
4922 project.last_formatting_failure.replace(error.to_string());
4923 }
4924 })?;
4925
4926 result
4927 })
4928 } else {
4929 let remote_id = self.remote_id();
4930 let client = self.client.clone();
4931 cx.spawn(move |this, mut cx| async move {
4932 if let Some(project_id) = remote_id {
4933 let response = client
4934 .request(proto::FormatBuffers {
4935 project_id,
4936 trigger: trigger as i32,
4937 buffer_ids: buffers
4938 .iter()
4939 .map(|buffer| {
4940 buffer.update(&mut cx, |buffer, _| buffer.remote_id().into())
4941 })
4942 .collect::<Result<_>>()?,
4943 })
4944 .await?
4945 .transaction
4946 .ok_or_else(|| anyhow!("missing transaction"))?;
4947 Self::deserialize_project_transaction(this, response, push_to_history, cx).await
4948 } else {
4949 Ok(ProjectTransaction::default())
4950 }
4951 })
4952 }
4953 }
4954
4955 async fn format_locally(
4956 project: WeakModel<Project>,
4957 mut buffers_with_paths: Vec<(Model<Buffer>, Option<PathBuf>)>,
4958 push_to_history: bool,
4959 trigger: FormatTrigger,
4960 mut cx: AsyncAppContext,
4961 ) -> anyhow::Result<ProjectTransaction> {
4962 // Do not allow multiple concurrent formatting requests for the
4963 // same buffer.
4964 project.update(&mut cx, |this, cx| {
4965 buffers_with_paths.retain(|(buffer, _)| {
4966 this.buffers_being_formatted
4967 .insert(buffer.read(cx).remote_id())
4968 });
4969 })?;
4970
4971 let _cleanup = defer({
4972 let this = project.clone();
4973 let mut cx = cx.clone();
4974 let buffers = &buffers_with_paths;
4975 move || {
4976 this.update(&mut cx, |this, cx| {
4977 for (buffer, _) in buffers {
4978 this.buffers_being_formatted
4979 .remove(&buffer.read(cx).remote_id());
4980 }
4981 })
4982 .ok();
4983 }
4984 });
4985
4986 let mut project_transaction = ProjectTransaction::default();
4987 for (buffer, buffer_abs_path) in &buffers_with_paths {
4988 let (primary_adapter_and_server, adapters_and_servers) =
4989 project.update(&mut cx, |project, cx| {
4990 let buffer = buffer.read(cx);
4991
4992 let adapters_and_servers = project
4993 .language_servers_for_buffer(buffer, cx)
4994 .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()))
4995 .collect::<Vec<_>>();
4996
4997 let primary_adapter = project
4998 .primary_language_server_for_buffer(buffer, cx)
4999 .map(|(adapter, lsp)| (adapter.clone(), lsp.clone()));
5000
5001 (primary_adapter, adapters_and_servers)
5002 })?;
5003
5004 let settings = buffer.update(&mut cx, |buffer, cx| {
5005 language_settings(buffer.language(), buffer.file(), cx).clone()
5006 })?;
5007
5008 let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
5009 let ensure_final_newline = settings.ensure_final_newline_on_save;
5010
5011 // First, format buffer's whitespace according to the settings.
5012 let trailing_whitespace_diff = if remove_trailing_whitespace {
5013 Some(
5014 buffer
5015 .update(&mut cx, |b, cx| b.remove_trailing_whitespace(cx))?
5016 .await,
5017 )
5018 } else {
5019 None
5020 };
5021 let whitespace_transaction_id = buffer.update(&mut cx, |buffer, cx| {
5022 buffer.finalize_last_transaction();
5023 buffer.start_transaction();
5024 if let Some(diff) = trailing_whitespace_diff {
5025 buffer.apply_diff(diff, cx);
5026 }
5027 if ensure_final_newline {
5028 buffer.ensure_final_newline(cx);
5029 }
5030 buffer.end_transaction(cx)
5031 })?;
5032
5033 // Apply the `code_actions_on_format` before we run the formatter.
5034 let code_actions = deserialize_code_actions(&settings.code_actions_on_format);
5035 #[allow(clippy::nonminimal_bool)]
5036 if !code_actions.is_empty()
5037 && !(trigger == FormatTrigger::Save && settings.format_on_save == FormatOnSave::Off)
5038 {
5039 Self::execute_code_actions_on_servers(
5040 &project,
5041 &adapters_and_servers,
5042 code_actions,
5043 buffer,
5044 push_to_history,
5045 &mut project_transaction,
5046 &mut cx,
5047 )
5048 .await?;
5049 }
5050
5051 // Apply language-specific formatting using either the primary language server
5052 // or external command.
5053 // Except for code actions, which are applied with all connected language servers.
5054 let primary_language_server =
5055 primary_adapter_and_server.map(|(_adapter, server)| server.clone());
5056 let server_and_buffer = primary_language_server
5057 .as_ref()
5058 .zip(buffer_abs_path.as_ref());
5059
5060 let prettier_settings = buffer.read_with(&mut cx, |buffer, cx| {
5061 language_settings(buffer.language(), buffer.file(), cx)
5062 .prettier
5063 .clone()
5064 })?;
5065
5066 let mut format_operations: Vec<FormatOperation> = vec![];
5067 {
5068 match trigger {
5069 FormatTrigger::Save => {
5070 match &settings.format_on_save {
5071 FormatOnSave::Off => {
5072 // nothing
5073 }
5074 FormatOnSave::On => {
5075 match &settings.formatter {
5076 SelectedFormatter::Auto => {
5077 // do the auto-format: prefer prettier, fallback to primary language server
5078 let diff = {
5079 if prettier_settings.allowed {
5080 Self::perform_format(
5081 &Formatter::Prettier,
5082 server_and_buffer,
5083 project.clone(),
5084 buffer,
5085 buffer_abs_path,
5086 &settings,
5087 &adapters_and_servers,
5088 push_to_history,
5089 &mut project_transaction,
5090 &mut cx,
5091 )
5092 .await
5093 } else {
5094 Self::perform_format(
5095 &Formatter::LanguageServer { name: None },
5096 server_and_buffer,
5097 project.clone(),
5098 buffer,
5099 buffer_abs_path,
5100 &settings,
5101 &adapters_and_servers,
5102 push_to_history,
5103 &mut project_transaction,
5104 &mut cx,
5105 )
5106 .await
5107 }
5108 }
5109 .log_err()
5110 .flatten();
5111 if let Some(op) = diff {
5112 format_operations.push(op);
5113 }
5114 }
5115 SelectedFormatter::List(formatters) => {
5116 for formatter in formatters.as_ref() {
5117 let diff = Self::perform_format(
5118 formatter,
5119 server_and_buffer,
5120 project.clone(),
5121 buffer,
5122 buffer_abs_path,
5123 &settings,
5124 &adapters_and_servers,
5125 push_to_history,
5126 &mut project_transaction,
5127 &mut cx,
5128 )
5129 .await
5130 .log_err()
5131 .flatten();
5132 if let Some(op) = diff {
5133 format_operations.push(op);
5134 }
5135
5136 // format with formatter
5137 }
5138 }
5139 }
5140 }
5141 FormatOnSave::List(formatters) => {
5142 for formatter in formatters.as_ref() {
5143 let diff = Self::perform_format(
5144 &formatter,
5145 server_and_buffer,
5146 project.clone(),
5147 buffer,
5148 buffer_abs_path,
5149 &settings,
5150 &adapters_and_servers,
5151 push_to_history,
5152 &mut project_transaction,
5153 &mut cx,
5154 )
5155 .await
5156 .log_err()
5157 .flatten();
5158 if let Some(op) = diff {
5159 format_operations.push(op);
5160 }
5161 }
5162 }
5163 }
5164 }
5165 FormatTrigger::Manual => {
5166 match &settings.formatter {
5167 SelectedFormatter::Auto => {
5168 // do the auto-format: prefer prettier, fallback to primary language server
5169 let diff = {
5170 if prettier_settings.allowed {
5171 Self::perform_format(
5172 &Formatter::Prettier,
5173 server_and_buffer,
5174 project.clone(),
5175 buffer,
5176 buffer_abs_path,
5177 &settings,
5178 &adapters_and_servers,
5179 push_to_history,
5180 &mut project_transaction,
5181 &mut cx,
5182 )
5183 .await
5184 } else {
5185 Self::perform_format(
5186 &Formatter::LanguageServer { name: None },
5187 server_and_buffer,
5188 project.clone(),
5189 buffer,
5190 buffer_abs_path,
5191 &settings,
5192 &adapters_and_servers,
5193 push_to_history,
5194 &mut project_transaction,
5195 &mut cx,
5196 )
5197 .await
5198 }
5199 }
5200 .log_err()
5201 .flatten();
5202
5203 if let Some(op) = diff {
5204 format_operations.push(op)
5205 }
5206 }
5207 SelectedFormatter::List(formatters) => {
5208 for formatter in formatters.as_ref() {
5209 // format with formatter
5210 let diff = Self::perform_format(
5211 formatter,
5212 server_and_buffer,
5213 project.clone(),
5214 buffer,
5215 buffer_abs_path,
5216 &settings,
5217 &adapters_and_servers,
5218 push_to_history,
5219 &mut project_transaction,
5220 &mut cx,
5221 )
5222 .await
5223 .log_err()
5224 .flatten();
5225 if let Some(op) = diff {
5226 format_operations.push(op);
5227 }
5228 }
5229 }
5230 }
5231 }
5232 }
5233 }
5234
5235 buffer.update(&mut cx, |b, cx| {
5236 // If the buffer had its whitespace formatted and was edited while the language-specific
5237 // formatting was being computed, avoid applying the language-specific formatting, because
5238 // it can't be grouped with the whitespace formatting in the undo history.
5239 if let Some(transaction_id) = whitespace_transaction_id {
5240 if b.peek_undo_stack()
5241 .map_or(true, |e| e.transaction_id() != transaction_id)
5242 {
5243 format_operations.clear();
5244 }
5245 }
5246
5247 // Apply any language-specific formatting, and group the two formatting operations
5248 // in the buffer's undo history.
5249 for operation in format_operations {
5250 match operation {
5251 FormatOperation::Lsp(edits) => {
5252 b.edit(edits, None, cx);
5253 }
5254 FormatOperation::External(diff) => {
5255 b.apply_diff(diff, cx);
5256 }
5257 FormatOperation::Prettier(diff) => {
5258 b.apply_diff(diff, cx);
5259 }
5260 }
5261
5262 if let Some(transaction_id) = whitespace_transaction_id {
5263 b.group_until_transaction(transaction_id);
5264 } else if let Some(transaction) = project_transaction.0.get(buffer) {
5265 b.group_until_transaction(transaction.id)
5266 }
5267 }
5268
5269 if let Some(transaction) = b.finalize_last_transaction().cloned() {
5270 if !push_to_history {
5271 b.forget_transaction(transaction.id);
5272 }
5273 project_transaction.0.insert(buffer.clone(), transaction);
5274 }
5275 })?;
5276 }
5277
5278 Ok(project_transaction)
5279 }
5280
5281 #[allow(clippy::too_many_arguments)]
5282 async fn perform_format(
5283 formatter: &Formatter,
5284 primary_server_and_buffer: Option<(&Arc<LanguageServer>, &PathBuf)>,
5285 project: WeakModel<Project>,
5286 buffer: &Model<Buffer>,
5287 buffer_abs_path: &Option<PathBuf>,
5288 settings: &LanguageSettings,
5289 adapters_and_servers: &Vec<(Arc<CachedLspAdapter>, Arc<LanguageServer>)>,
5290 push_to_history: bool,
5291 transaction: &mut ProjectTransaction,
5292 mut cx: &mut AsyncAppContext,
5293 ) -> Result<Option<FormatOperation>, anyhow::Error> {
5294 let result = match formatter {
5295 Formatter::LanguageServer { name } => {
5296 if let Some((language_server, buffer_abs_path)) = primary_server_and_buffer {
5297 let language_server = if let Some(name) = name {
5298 adapters_and_servers
5299 .iter()
5300 .find_map(|(adapter, server)| {
5301 adapter.name.0.as_ref().eq(name.as_str()).then_some(server)
5302 })
5303 .unwrap_or_else(|| language_server)
5304 } else {
5305 language_server
5306 };
5307 Some(FormatOperation::Lsp(
5308 Self::format_via_lsp(
5309 &project,
5310 buffer,
5311 buffer_abs_path,
5312 language_server,
5313 settings,
5314 cx,
5315 )
5316 .await
5317 .context("failed to format via language server")?,
5318 ))
5319 } else {
5320 None
5321 }
5322 }
5323 Formatter::Prettier => {
5324 prettier_support::format_with_prettier(&project, buffer, &mut cx)
5325 .await
5326 .transpose()
5327 .ok()
5328 .flatten()
5329 }
5330 Formatter::External { command, arguments } => {
5331 let buffer_abs_path = buffer_abs_path.as_ref().map(|path| path.as_path());
5332 Self::format_via_external_command(
5333 buffer,
5334 buffer_abs_path,
5335 &command,
5336 &arguments,
5337 &mut cx,
5338 )
5339 .await
5340 .context(format!(
5341 "failed to format via external command {:?}",
5342 command
5343 ))?
5344 .map(FormatOperation::External)
5345 }
5346 Formatter::CodeActions(code_actions) => {
5347 let code_actions = deserialize_code_actions(&code_actions);
5348 if !code_actions.is_empty() {
5349 Self::execute_code_actions_on_servers(
5350 &project,
5351 &adapters_and_servers,
5352 code_actions,
5353 buffer,
5354 push_to_history,
5355 transaction,
5356 cx,
5357 )
5358 .await?;
5359 }
5360 None
5361 }
5362 };
5363 anyhow::Ok(result)
5364 }
5365
5366 async fn format_via_lsp(
5367 this: &WeakModel<Self>,
5368 buffer: &Model<Buffer>,
5369 abs_path: &Path,
5370 language_server: &Arc<LanguageServer>,
5371 settings: &LanguageSettings,
5372 cx: &mut AsyncAppContext,
5373 ) -> Result<Vec<(Range<Anchor>, String)>> {
5374 let uri = lsp::Url::from_file_path(abs_path)
5375 .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
5376 let text_document = lsp::TextDocumentIdentifier::new(uri);
5377 let capabilities = &language_server.capabilities();
5378
5379 let formatting_provider = capabilities.document_formatting_provider.as_ref();
5380 let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
5381
5382 let lsp_edits = if matches!(formatting_provider, Some(p) if *p != OneOf::Left(false)) {
5383 language_server
5384 .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
5385 text_document,
5386 options: lsp_command::lsp_formatting_options(settings),
5387 work_done_progress_params: Default::default(),
5388 })
5389 .await?
5390 } else if matches!(range_formatting_provider, Some(p) if *p != OneOf::Left(false)) {
5391 let buffer_start = lsp::Position::new(0, 0);
5392 let buffer_end = buffer.update(cx, |b, _| point_to_lsp(b.max_point_utf16()))?;
5393
5394 language_server
5395 .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
5396 text_document,
5397 range: lsp::Range::new(buffer_start, buffer_end),
5398 options: lsp_command::lsp_formatting_options(settings),
5399 work_done_progress_params: Default::default(),
5400 })
5401 .await?
5402 } else {
5403 None
5404 };
5405
5406 if let Some(lsp_edits) = lsp_edits {
5407 this.update(cx, |this, cx| {
5408 this.edits_from_lsp(buffer, lsp_edits, language_server.server_id(), None, cx)
5409 })?
5410 .await
5411 } else {
5412 Ok(Vec::new())
5413 }
5414 }
5415
5416 async fn format_via_external_command(
5417 buffer: &Model<Buffer>,
5418 buffer_abs_path: Option<&Path>,
5419 command: &str,
5420 arguments: &[String],
5421 cx: &mut AsyncAppContext,
5422 ) -> Result<Option<Diff>> {
5423 let working_dir_path = buffer.update(cx, |buffer, cx| {
5424 let file = File::from_dyn(buffer.file())?;
5425 let worktree = file.worktree.read(cx);
5426 let mut worktree_path = worktree.abs_path().to_path_buf();
5427 if worktree.root_entry()?.is_file() {
5428 worktree_path.pop();
5429 }
5430 Some(worktree_path)
5431 })?;
5432
5433 let mut child = smol::process::Command::new(command);
5434
5435 if let Some(working_dir_path) = working_dir_path {
5436 child.current_dir(working_dir_path);
5437 }
5438
5439 let mut child = child
5440 .args(arguments.iter().map(|arg| {
5441 if let Some(buffer_abs_path) = buffer_abs_path {
5442 arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy())
5443 } else {
5444 arg.replace("{buffer_path}", "Untitled")
5445 }
5446 }))
5447 .stdin(smol::process::Stdio::piped())
5448 .stdout(smol::process::Stdio::piped())
5449 .stderr(smol::process::Stdio::piped())
5450 .spawn()?;
5451
5452 let stdin = child
5453 .stdin
5454 .as_mut()
5455 .ok_or_else(|| anyhow!("failed to acquire stdin"))?;
5456 let text = buffer.update(cx, |buffer, _| buffer.as_rope().clone())?;
5457 for chunk in text.chunks() {
5458 stdin.write_all(chunk.as_bytes()).await?;
5459 }
5460 stdin.flush().await?;
5461
5462 let output = child.output().await?;
5463 if !output.status.success() {
5464 return Err(anyhow!(
5465 "command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
5466 output.status.code(),
5467 String::from_utf8_lossy(&output.stdout),
5468 String::from_utf8_lossy(&output.stderr),
5469 ));
5470 }
5471
5472 let stdout = String::from_utf8(output.stdout)?;
5473 Ok(Some(
5474 buffer
5475 .update(cx, |buffer, cx| buffer.diff(stdout, cx))?
5476 .await,
5477 ))
5478 }
5479
5480 #[inline(never)]
5481 fn definition_impl(
5482 &self,
5483 buffer: &Model<Buffer>,
5484 position: PointUtf16,
5485 cx: &mut ModelContext<Self>,
5486 ) -> Task<Result<Vec<LocationLink>>> {
5487 self.request_lsp(
5488 buffer.clone(),
5489 LanguageServerToQuery::Primary,
5490 GetDefinition { position },
5491 cx,
5492 )
5493 }
5494 pub fn definition<T: ToPointUtf16>(
5495 &self,
5496 buffer: &Model<Buffer>,
5497 position: T,
5498 cx: &mut ModelContext<Self>,
5499 ) -> Task<Result<Vec<LocationLink>>> {
5500 let position = position.to_point_utf16(buffer.read(cx));
5501 self.definition_impl(buffer, position, cx)
5502 }
5503
5504 fn declaration_impl(
5505 &self,
5506 buffer: &Model<Buffer>,
5507 position: PointUtf16,
5508 cx: &mut ModelContext<Self>,
5509 ) -> Task<Result<Vec<LocationLink>>> {
5510 self.request_lsp(
5511 buffer.clone(),
5512 LanguageServerToQuery::Primary,
5513 GetDeclaration { position },
5514 cx,
5515 )
5516 }
5517
5518 pub fn declaration<T: ToPointUtf16>(
5519 &self,
5520 buffer: &Model<Buffer>,
5521 position: T,
5522 cx: &mut ModelContext<Self>,
5523 ) -> Task<Result<Vec<LocationLink>>> {
5524 let position = position.to_point_utf16(buffer.read(cx));
5525 self.declaration_impl(buffer, position, cx)
5526 }
5527
5528 fn type_definition_impl(
5529 &self,
5530 buffer: &Model<Buffer>,
5531 position: PointUtf16,
5532 cx: &mut ModelContext<Self>,
5533 ) -> Task<Result<Vec<LocationLink>>> {
5534 self.request_lsp(
5535 buffer.clone(),
5536 LanguageServerToQuery::Primary,
5537 GetTypeDefinition { position },
5538 cx,
5539 )
5540 }
5541
5542 pub fn type_definition<T: ToPointUtf16>(
5543 &self,
5544 buffer: &Model<Buffer>,
5545 position: T,
5546 cx: &mut ModelContext<Self>,
5547 ) -> Task<Result<Vec<LocationLink>>> {
5548 let position = position.to_point_utf16(buffer.read(cx));
5549 self.type_definition_impl(buffer, position, cx)
5550 }
5551
5552 fn implementation_impl(
5553 &self,
5554 buffer: &Model<Buffer>,
5555 position: PointUtf16,
5556 cx: &mut ModelContext<Self>,
5557 ) -> Task<Result<Vec<LocationLink>>> {
5558 self.request_lsp(
5559 buffer.clone(),
5560 LanguageServerToQuery::Primary,
5561 GetImplementation { position },
5562 cx,
5563 )
5564 }
5565
5566 pub fn implementation<T: ToPointUtf16>(
5567 &self,
5568 buffer: &Model<Buffer>,
5569 position: T,
5570 cx: &mut ModelContext<Self>,
5571 ) -> Task<Result<Vec<LocationLink>>> {
5572 let position = position.to_point_utf16(buffer.read(cx));
5573 self.implementation_impl(buffer, position, cx)
5574 }
5575
5576 fn references_impl(
5577 &self,
5578 buffer: &Model<Buffer>,
5579 position: PointUtf16,
5580 cx: &mut ModelContext<Self>,
5581 ) -> Task<Result<Vec<Location>>> {
5582 self.request_lsp(
5583 buffer.clone(),
5584 LanguageServerToQuery::Primary,
5585 GetReferences { position },
5586 cx,
5587 )
5588 }
5589 pub fn references<T: ToPointUtf16>(
5590 &self,
5591 buffer: &Model<Buffer>,
5592 position: T,
5593 cx: &mut ModelContext<Self>,
5594 ) -> Task<Result<Vec<Location>>> {
5595 let position = position.to_point_utf16(buffer.read(cx));
5596 self.references_impl(buffer, position, cx)
5597 }
5598
5599 fn document_highlights_impl(
5600 &self,
5601 buffer: &Model<Buffer>,
5602 position: PointUtf16,
5603 cx: &mut ModelContext<Self>,
5604 ) -> Task<Result<Vec<DocumentHighlight>>> {
5605 self.request_lsp(
5606 buffer.clone(),
5607 LanguageServerToQuery::Primary,
5608 GetDocumentHighlights { position },
5609 cx,
5610 )
5611 }
5612
5613 pub fn document_highlights<T: ToPointUtf16>(
5614 &self,
5615 buffer: &Model<Buffer>,
5616 position: T,
5617 cx: &mut ModelContext<Self>,
5618 ) -> Task<Result<Vec<DocumentHighlight>>> {
5619 let position = position.to_point_utf16(buffer.read(cx));
5620 self.document_highlights_impl(buffer, position, cx)
5621 }
5622
5623 pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
5624 let language_registry = self.languages.clone();
5625
5626 if self.is_local_or_ssh() {
5627 let mut requests = Vec::new();
5628 for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
5629 let Some(worktree_handle) = self.worktree_for_id(*worktree_id, cx) else {
5630 continue;
5631 };
5632 let worktree = worktree_handle.read(cx);
5633 if !worktree.is_visible() {
5634 continue;
5635 }
5636 let worktree_abs_path = worktree.abs_path().clone();
5637
5638 let (adapter, language, server) = match self.language_servers.get(server_id) {
5639 Some(LanguageServerState::Running {
5640 adapter,
5641 language,
5642 server,
5643 ..
5644 }) => (adapter.clone(), language.clone(), server),
5645
5646 _ => continue,
5647 };
5648
5649 requests.push(
5650 server
5651 .request::<lsp::request::WorkspaceSymbolRequest>(
5652 lsp::WorkspaceSymbolParams {
5653 query: query.to_string(),
5654 ..Default::default()
5655 },
5656 )
5657 .log_err()
5658 .map(move |response| {
5659 let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response {
5660 lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
5661 flat_responses.into_iter().map(|lsp_symbol| {
5662 (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
5663 }).collect::<Vec<_>>()
5664 }
5665 lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
5666 nested_responses.into_iter().filter_map(|lsp_symbol| {
5667 let location = match lsp_symbol.location {
5668 OneOf::Left(location) => location,
5669 OneOf::Right(_) => {
5670 error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport");
5671 return None
5672 }
5673 };
5674 Some((lsp_symbol.name, lsp_symbol.kind, location))
5675 }).collect::<Vec<_>>()
5676 }
5677 }).unwrap_or_default();
5678
5679 (
5680 adapter,
5681 language,
5682 worktree_handle.downgrade(),
5683 worktree_abs_path,
5684 lsp_symbols,
5685 )
5686 }),
5687 );
5688 }
5689
5690 cx.spawn(move |this, mut cx| async move {
5691 let responses = futures::future::join_all(requests).await;
5692 let this = match this.upgrade() {
5693 Some(this) => this,
5694 None => return Ok(Vec::new()),
5695 };
5696
5697 let mut symbols = Vec::new();
5698 for (adapter, adapter_language, source_worktree, worktree_abs_path, lsp_symbols) in
5699 responses
5700 {
5701 let core_symbols = this.update(&mut cx, |this, cx| {
5702 lsp_symbols
5703 .into_iter()
5704 .filter_map(|(symbol_name, symbol_kind, symbol_location)| {
5705 let abs_path = symbol_location.uri.to_file_path().ok()?;
5706 let source_worktree = source_worktree.upgrade()?;
5707 let source_worktree_id = source_worktree.read(cx).id();
5708
5709 let path;
5710 let worktree;
5711 if let Some((tree, rel_path)) = this.find_worktree(&abs_path, cx) {
5712 worktree = tree;
5713 path = rel_path;
5714 } else {
5715 worktree = source_worktree.clone();
5716 path = relativize_path(&worktree_abs_path, &abs_path);
5717 }
5718
5719 let worktree_id = worktree.read(cx).id();
5720 let project_path = ProjectPath {
5721 worktree_id,
5722 path: path.into(),
5723 };
5724 let signature = this.symbol_signature(&project_path);
5725 Some(CoreSymbol {
5726 language_server_name: adapter.name.clone(),
5727 source_worktree_id,
5728 path: project_path,
5729 kind: symbol_kind,
5730 name: symbol_name,
5731 range: range_from_lsp(symbol_location.range),
5732 signature,
5733 })
5734 })
5735 .collect()
5736 })?;
5737
5738 populate_labels_for_symbols(
5739 core_symbols,
5740 &language_registry,
5741 Some(adapter_language),
5742 Some(adapter),
5743 &mut symbols,
5744 )
5745 .await;
5746 }
5747
5748 Ok(symbols)
5749 })
5750 } else if let Some(project_id) = self.remote_id() {
5751 let request = self.client.request(proto::GetProjectSymbols {
5752 project_id,
5753 query: query.to_string(),
5754 });
5755 cx.foreground_executor().spawn(async move {
5756 let response = request.await?;
5757 let mut symbols = Vec::new();
5758 let core_symbols = response
5759 .symbols
5760 .into_iter()
5761 .filter_map(|symbol| Self::deserialize_symbol(symbol).log_err())
5762 .collect::<Vec<_>>();
5763 populate_labels_for_symbols(
5764 core_symbols,
5765 &language_registry,
5766 None,
5767 None,
5768 &mut symbols,
5769 )
5770 .await;
5771 Ok(symbols)
5772 })
5773 } else {
5774 Task::ready(Ok(Default::default()))
5775 }
5776 }
5777
5778 pub fn open_buffer_for_symbol(
5779 &mut self,
5780 symbol: &Symbol,
5781 cx: &mut ModelContext<Self>,
5782 ) -> Task<Result<Model<Buffer>>> {
5783 if self.is_local_or_ssh() {
5784 let language_server_id = if let Some(id) = self.language_server_ids.get(&(
5785 symbol.source_worktree_id,
5786 symbol.language_server_name.clone(),
5787 )) {
5788 *id
5789 } else {
5790 return Task::ready(Err(anyhow!(
5791 "language server for worktree and language not found"
5792 )));
5793 };
5794
5795 let worktree_abs_path = if let Some(worktree_abs_path) = self
5796 .worktree_for_id(symbol.path.worktree_id, cx)
5797 .map(|worktree| worktree.read(cx).abs_path())
5798 {
5799 worktree_abs_path
5800 } else {
5801 return Task::ready(Err(anyhow!("worktree not found for symbol")));
5802 };
5803
5804 let symbol_abs_path = resolve_path(&worktree_abs_path, &symbol.path.path);
5805 let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
5806 uri
5807 } else {
5808 return Task::ready(Err(anyhow!("invalid symbol path")));
5809 };
5810
5811 self.open_local_buffer_via_lsp(
5812 symbol_uri,
5813 language_server_id,
5814 symbol.language_server_name.clone(),
5815 cx,
5816 )
5817 } else if let Some(project_id) = self.remote_id() {
5818 let request = self.client.request(proto::OpenBufferForSymbol {
5819 project_id,
5820 symbol: Some(serialize_symbol(symbol)),
5821 });
5822 cx.spawn(move |this, mut cx| async move {
5823 let response = request.await?;
5824 let buffer_id = BufferId::new(response.buffer_id)?;
5825 this.update(&mut cx, |this, cx| {
5826 this.wait_for_remote_buffer(buffer_id, cx)
5827 })?
5828 .await
5829 })
5830 } else {
5831 Task::ready(Err(anyhow!("project does not have a remote id")))
5832 }
5833 }
5834
5835 pub fn signature_help<T: ToPointUtf16>(
5836 &self,
5837 buffer: &Model<Buffer>,
5838 position: T,
5839 cx: &mut ModelContext<Self>,
5840 ) -> Task<Vec<SignatureHelp>> {
5841 let position = position.to_point_utf16(buffer.read(cx));
5842 if self.is_local_or_ssh() {
5843 let all_actions_task = self.request_multiple_lsp_locally(
5844 buffer,
5845 Some(position),
5846 GetSignatureHelp { position },
5847 cx,
5848 );
5849 cx.spawn(|_, _| async move {
5850 all_actions_task
5851 .await
5852 .into_iter()
5853 .flatten()
5854 .filter(|help| !help.markdown.is_empty())
5855 .collect::<Vec<_>>()
5856 })
5857 } else if let Some(project_id) = self.remote_id() {
5858 let request_task = self.client().request(proto::MultiLspQuery {
5859 buffer_id: buffer.read(cx).remote_id().into(),
5860 version: serialize_version(&buffer.read(cx).version()),
5861 project_id,
5862 strategy: Some(proto::multi_lsp_query::Strategy::All(
5863 proto::AllLanguageServers {},
5864 )),
5865 request: Some(proto::multi_lsp_query::Request::GetSignatureHelp(
5866 GetSignatureHelp { position }.to_proto(project_id, buffer.read(cx)),
5867 )),
5868 });
5869 let buffer = buffer.clone();
5870 cx.spawn(|weak_project, cx| async move {
5871 let Some(project) = weak_project.upgrade() else {
5872 return Vec::new();
5873 };
5874 join_all(
5875 request_task
5876 .await
5877 .log_err()
5878 .map(|response| response.responses)
5879 .unwrap_or_default()
5880 .into_iter()
5881 .filter_map(|lsp_response| match lsp_response.response? {
5882 proto::lsp_response::Response::GetSignatureHelpResponse(response) => {
5883 Some(response)
5884 }
5885 unexpected => {
5886 debug_panic!("Unexpected response: {unexpected:?}");
5887 None
5888 }
5889 })
5890 .map(|signature_response| {
5891 let response = GetSignatureHelp { position }.response_from_proto(
5892 signature_response,
5893 project.clone(),
5894 buffer.clone(),
5895 cx.clone(),
5896 );
5897 async move { response.await.log_err().flatten() }
5898 }),
5899 )
5900 .await
5901 .into_iter()
5902 .flatten()
5903 .collect()
5904 })
5905 } else {
5906 Task::ready(Vec::new())
5907 }
5908 }
5909
5910 fn hover_impl(
5911 &self,
5912 buffer: &Model<Buffer>,
5913 position: PointUtf16,
5914 cx: &mut ModelContext<Self>,
5915 ) -> Task<Vec<Hover>> {
5916 if self.is_local_or_ssh() {
5917 let all_actions_task = self.request_multiple_lsp_locally(
5918 &buffer,
5919 Some(position),
5920 GetHover { position },
5921 cx,
5922 );
5923 cx.spawn(|_, _| async move {
5924 all_actions_task
5925 .await
5926 .into_iter()
5927 .filter_map(|hover| remove_empty_hover_blocks(hover?))
5928 .collect::<Vec<Hover>>()
5929 })
5930 } else if let Some(project_id) = self.remote_id() {
5931 let request_task = self.client().request(proto::MultiLspQuery {
5932 buffer_id: buffer.read(cx).remote_id().into(),
5933 version: serialize_version(&buffer.read(cx).version()),
5934 project_id,
5935 strategy: Some(proto::multi_lsp_query::Strategy::All(
5936 proto::AllLanguageServers {},
5937 )),
5938 request: Some(proto::multi_lsp_query::Request::GetHover(
5939 GetHover { position }.to_proto(project_id, buffer.read(cx)),
5940 )),
5941 });
5942 let buffer = buffer.clone();
5943 cx.spawn(|weak_project, cx| async move {
5944 let Some(project) = weak_project.upgrade() else {
5945 return Vec::new();
5946 };
5947 join_all(
5948 request_task
5949 .await
5950 .log_err()
5951 .map(|response| response.responses)
5952 .unwrap_or_default()
5953 .into_iter()
5954 .filter_map(|lsp_response| match lsp_response.response? {
5955 proto::lsp_response::Response::GetHoverResponse(response) => {
5956 Some(response)
5957 }
5958 unexpected => {
5959 debug_panic!("Unexpected response: {unexpected:?}");
5960 None
5961 }
5962 })
5963 .map(|hover_response| {
5964 let response = GetHover { position }.response_from_proto(
5965 hover_response,
5966 project.clone(),
5967 buffer.clone(),
5968 cx.clone(),
5969 );
5970 async move {
5971 response
5972 .await
5973 .log_err()
5974 .flatten()
5975 .and_then(remove_empty_hover_blocks)
5976 }
5977 }),
5978 )
5979 .await
5980 .into_iter()
5981 .flatten()
5982 .collect()
5983 })
5984 } else {
5985 log::error!("cannot show hovers: project does not have a remote id");
5986 Task::ready(Vec::new())
5987 }
5988 }
5989
5990 pub fn hover<T: ToPointUtf16>(
5991 &self,
5992 buffer: &Model<Buffer>,
5993 position: T,
5994 cx: &mut ModelContext<Self>,
5995 ) -> Task<Vec<Hover>> {
5996 let position = position.to_point_utf16(buffer.read(cx));
5997 self.hover_impl(buffer, position, cx)
5998 }
5999
6000 fn linked_edit_impl(
6001 &self,
6002 buffer: &Model<Buffer>,
6003 position: Anchor,
6004 cx: &mut ModelContext<Self>,
6005 ) -> Task<Result<Vec<Range<Anchor>>>> {
6006 let snapshot = buffer.read(cx).snapshot();
6007 let scope = snapshot.language_scope_at(position);
6008 let Some(server_id) = self
6009 .language_servers_for_buffer(buffer.read(cx), cx)
6010 .filter(|(_, server)| {
6011 server
6012 .capabilities()
6013 .linked_editing_range_provider
6014 .is_some()
6015 })
6016 .filter(|(adapter, _)| {
6017 scope
6018 .as_ref()
6019 .map(|scope| scope.language_allowed(&adapter.name))
6020 .unwrap_or(true)
6021 })
6022 .map(|(_, server)| LanguageServerToQuery::Other(server.server_id()))
6023 .next()
6024 .or_else(|| {
6025 self.is_via_collab()
6026 .then_some(LanguageServerToQuery::Primary)
6027 })
6028 .filter(|_| {
6029 maybe!({
6030 let language_name = buffer.read(cx).language_at(position)?.name();
6031 Some(
6032 AllLanguageSettings::get_global(cx)
6033 .language(Some(&language_name))
6034 .linked_edits,
6035 )
6036 }) == Some(true)
6037 })
6038 else {
6039 return Task::ready(Ok(vec![]));
6040 };
6041
6042 self.request_lsp(
6043 buffer.clone(),
6044 server_id,
6045 LinkedEditingRange { position },
6046 cx,
6047 )
6048 }
6049
6050 pub fn linked_edit(
6051 &self,
6052 buffer: &Model<Buffer>,
6053 position: Anchor,
6054 cx: &mut ModelContext<Self>,
6055 ) -> Task<Result<Vec<Range<Anchor>>>> {
6056 self.linked_edit_impl(buffer, position, cx)
6057 }
6058
6059 #[inline(never)]
6060 fn completions_impl(
6061 &self,
6062 buffer: &Model<Buffer>,
6063 position: PointUtf16,
6064 context: CompletionContext,
6065 cx: &mut ModelContext<Self>,
6066 ) -> Task<Result<Vec<Completion>>> {
6067 let language_registry = self.languages.clone();
6068
6069 if self.is_local_or_ssh() {
6070 let snapshot = buffer.read(cx).snapshot();
6071 let offset = position.to_offset(&snapshot);
6072 let scope = snapshot.language_scope_at(offset);
6073 let language = snapshot.language().cloned();
6074
6075 let server_ids: Vec<_> = self
6076 .language_servers_for_buffer(buffer.read(cx), cx)
6077 .filter(|(_, server)| server.capabilities().completion_provider.is_some())
6078 .filter(|(adapter, _)| {
6079 scope
6080 .as_ref()
6081 .map(|scope| scope.language_allowed(&adapter.name))
6082 .unwrap_or(true)
6083 })
6084 .map(|(_, server)| server.server_id())
6085 .collect();
6086
6087 let buffer = buffer.clone();
6088 cx.spawn(move |this, mut cx| async move {
6089 let mut tasks = Vec::with_capacity(server_ids.len());
6090 this.update(&mut cx, |this, cx| {
6091 for server_id in server_ids {
6092 let lsp_adapter = this.language_server_adapter_for_id(server_id);
6093 tasks.push((
6094 lsp_adapter,
6095 this.request_lsp(
6096 buffer.clone(),
6097 LanguageServerToQuery::Other(server_id),
6098 GetCompletions {
6099 position,
6100 context: context.clone(),
6101 },
6102 cx,
6103 ),
6104 ));
6105 }
6106 })?;
6107
6108 let mut completions = Vec::new();
6109 for (lsp_adapter, task) in tasks {
6110 if let Ok(new_completions) = task.await {
6111 populate_labels_for_completions(
6112 new_completions,
6113 &language_registry,
6114 language.clone(),
6115 lsp_adapter,
6116 &mut completions,
6117 )
6118 .await;
6119 }
6120 }
6121
6122 Ok(completions)
6123 })
6124 } else if let Some(project_id) = self.remote_id() {
6125 let task = self.send_lsp_proto_request(
6126 buffer.clone(),
6127 project_id,
6128 GetCompletions { position, context },
6129 cx,
6130 );
6131 let language = buffer.read(cx).language().cloned();
6132
6133 // In the future, we should provide project guests with the names of LSP adapters,
6134 // so that they can use the correct LSP adapter when computing labels. For now,
6135 // guests just use the first LSP adapter associated with the buffer's language.
6136 let lsp_adapter = language
6137 .as_ref()
6138 .and_then(|language| language_registry.lsp_adapters(language).first().cloned());
6139
6140 cx.foreground_executor().spawn(async move {
6141 let completions = task.await?;
6142 let mut result = Vec::new();
6143 populate_labels_for_completions(
6144 completions,
6145 &language_registry,
6146 language,
6147 lsp_adapter,
6148 &mut result,
6149 )
6150 .await;
6151 Ok(result)
6152 })
6153 } else {
6154 Task::ready(Ok(Default::default()))
6155 }
6156 }
6157
6158 pub fn completions<T: ToOffset + ToPointUtf16>(
6159 &self,
6160 buffer: &Model<Buffer>,
6161 position: T,
6162 context: CompletionContext,
6163 cx: &mut ModelContext<Self>,
6164 ) -> Task<Result<Vec<Completion>>> {
6165 let position = position.to_point_utf16(buffer.read(cx));
6166 self.completions_impl(buffer, position, context, cx)
6167 }
6168
6169 pub fn resolve_completions(
6170 &self,
6171 buffer: Model<Buffer>,
6172 completion_indices: Vec<usize>,
6173 completions: Arc<RwLock<Box<[Completion]>>>,
6174 cx: &mut ModelContext<Self>,
6175 ) -> Task<Result<bool>> {
6176 let client = self.client();
6177 let language_registry = self.languages().clone();
6178
6179 let is_remote = self.is_via_collab();
6180 let project_id = self.remote_id();
6181
6182 let buffer_id = buffer.read(cx).remote_id();
6183 let buffer_snapshot = buffer.read(cx).snapshot();
6184
6185 cx.spawn(move |this, mut cx| async move {
6186 let mut did_resolve = false;
6187 if is_remote {
6188 let project_id =
6189 project_id.ok_or_else(|| anyhow!("Remote project without remote_id"))?;
6190
6191 for completion_index in completion_indices {
6192 let (server_id, completion) = {
6193 let completions_guard = completions.read();
6194 let completion = &completions_guard[completion_index];
6195 if completion.documentation.is_some() {
6196 continue;
6197 }
6198
6199 did_resolve = true;
6200 let server_id = completion.server_id;
6201 let completion = completion.lsp_completion.clone();
6202
6203 (server_id, completion)
6204 };
6205
6206 Self::resolve_completion_remote(
6207 project_id,
6208 server_id,
6209 buffer_id,
6210 completions.clone(),
6211 completion_index,
6212 completion,
6213 client.clone(),
6214 language_registry.clone(),
6215 )
6216 .await;
6217 }
6218 } else {
6219 for completion_index in completion_indices {
6220 let (server_id, completion) = {
6221 let completions_guard = completions.read();
6222 let completion = &completions_guard[completion_index];
6223 if completion.documentation.is_some() {
6224 continue;
6225 }
6226
6227 let server_id = completion.server_id;
6228 let completion = completion.lsp_completion.clone();
6229
6230 (server_id, completion)
6231 };
6232
6233 let server = this
6234 .read_with(&mut cx, |project, _| {
6235 project.language_server_for_id(server_id)
6236 })
6237 .ok()
6238 .flatten();
6239 let Some(server) = server else {
6240 continue;
6241 };
6242
6243 did_resolve = true;
6244 Self::resolve_completion_local(
6245 server,
6246 &buffer_snapshot,
6247 completions.clone(),
6248 completion_index,
6249 completion,
6250 language_registry.clone(),
6251 )
6252 .await;
6253 }
6254 }
6255
6256 Ok(did_resolve)
6257 })
6258 }
6259
6260 async fn resolve_completion_local(
6261 server: Arc<lsp::LanguageServer>,
6262 snapshot: &BufferSnapshot,
6263 completions: Arc<RwLock<Box<[Completion]>>>,
6264 completion_index: usize,
6265 completion: lsp::CompletionItem,
6266 language_registry: Arc<LanguageRegistry>,
6267 ) {
6268 let can_resolve = server
6269 .capabilities()
6270 .completion_provider
6271 .as_ref()
6272 .and_then(|options| options.resolve_provider)
6273 .unwrap_or(false);
6274 if !can_resolve {
6275 return;
6276 }
6277
6278 let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
6279 let Some(completion_item) = request.await.log_err() else {
6280 return;
6281 };
6282
6283 if let Some(lsp_documentation) = completion_item.documentation.as_ref() {
6284 let documentation = language::prepare_completion_documentation(
6285 lsp_documentation,
6286 &language_registry,
6287 None, // TODO: Try to reasonably work out which language the completion is for
6288 )
6289 .await;
6290
6291 let mut completions = completions.write();
6292 let completion = &mut completions[completion_index];
6293 completion.documentation = Some(documentation);
6294 } else {
6295 let mut completions = completions.write();
6296 let completion = &mut completions[completion_index];
6297 completion.documentation = Some(Documentation::Undocumented);
6298 }
6299
6300 if let Some(text_edit) = completion_item.text_edit.as_ref() {
6301 // Technically we don't have to parse the whole `text_edit`, since the only
6302 // language server we currently use that does update `text_edit` in `completionItem/resolve`
6303 // is `typescript-language-server` and they only update `text_edit.new_text`.
6304 // But we should not rely on that.
6305 let edit = parse_completion_text_edit(text_edit, snapshot);
6306
6307 if let Some((old_range, mut new_text)) = edit {
6308 LineEnding::normalize(&mut new_text);
6309
6310 let mut completions = completions.write();
6311 let completion = &mut completions[completion_index];
6312
6313 completion.new_text = new_text;
6314 completion.old_range = old_range;
6315 }
6316 }
6317 if completion_item.insert_text_format == Some(InsertTextFormat::SNIPPET) {
6318 // vtsls might change the type of completion after resolution.
6319 let mut completions = completions.write();
6320 let completion = &mut completions[completion_index];
6321 if completion_item.insert_text_format != completion.lsp_completion.insert_text_format {
6322 completion.lsp_completion.insert_text_format = completion_item.insert_text_format;
6323 }
6324 }
6325 }
6326
6327 #[allow(clippy::too_many_arguments)]
6328 async fn resolve_completion_remote(
6329 project_id: u64,
6330 server_id: LanguageServerId,
6331 buffer_id: BufferId,
6332 completions: Arc<RwLock<Box<[Completion]>>>,
6333 completion_index: usize,
6334 completion: lsp::CompletionItem,
6335 client: Arc<Client>,
6336 language_registry: Arc<LanguageRegistry>,
6337 ) {
6338 let request = proto::ResolveCompletionDocumentation {
6339 project_id,
6340 language_server_id: server_id.0 as u64,
6341 lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
6342 buffer_id: buffer_id.into(),
6343 };
6344
6345 let Some(response) = client
6346 .request(request)
6347 .await
6348 .context("completion documentation resolve proto request")
6349 .log_err()
6350 else {
6351 return;
6352 };
6353
6354 let documentation = if response.documentation.is_empty() {
6355 Documentation::Undocumented
6356 } else if response.documentation_is_markdown {
6357 Documentation::MultiLineMarkdown(
6358 markdown::parse_markdown(&response.documentation, &language_registry, None).await,
6359 )
6360 } else if response.documentation.lines().count() <= 1 {
6361 Documentation::SingleLine(response.documentation)
6362 } else {
6363 Documentation::MultiLinePlainText(response.documentation)
6364 };
6365
6366 let mut completions = completions.write();
6367 let completion = &mut completions[completion_index];
6368 completion.documentation = Some(documentation);
6369
6370 let old_range = response
6371 .old_start
6372 .and_then(deserialize_anchor)
6373 .zip(response.old_end.and_then(deserialize_anchor));
6374 if let Some((old_start, old_end)) = old_range {
6375 if !response.new_text.is_empty() {
6376 completion.new_text = response.new_text;
6377 completion.old_range = old_start..old_end;
6378 }
6379 }
6380 }
6381
6382 pub fn apply_additional_edits_for_completion(
6383 &self,
6384 buffer_handle: Model<Buffer>,
6385 completion: Completion,
6386 push_to_history: bool,
6387 cx: &mut ModelContext<Self>,
6388 ) -> Task<Result<Option<Transaction>>> {
6389 let buffer = buffer_handle.read(cx);
6390 let buffer_id = buffer.remote_id();
6391
6392 if self.is_local_or_ssh() {
6393 let server_id = completion.server_id;
6394 let lang_server = match self.language_server_for_buffer(buffer, server_id, cx) {
6395 Some((_, server)) => server.clone(),
6396 _ => return Task::ready(Ok(Default::default())),
6397 };
6398
6399 cx.spawn(move |this, mut cx| async move {
6400 let can_resolve = lang_server
6401 .capabilities()
6402 .completion_provider
6403 .as_ref()
6404 .and_then(|options| options.resolve_provider)
6405 .unwrap_or(false);
6406 let additional_text_edits = if can_resolve {
6407 lang_server
6408 .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
6409 .await?
6410 .additional_text_edits
6411 } else {
6412 completion.lsp_completion.additional_text_edits
6413 };
6414 if let Some(edits) = additional_text_edits {
6415 let edits = this
6416 .update(&mut cx, |this, cx| {
6417 this.edits_from_lsp(
6418 &buffer_handle,
6419 edits,
6420 lang_server.server_id(),
6421 None,
6422 cx,
6423 )
6424 })?
6425 .await?;
6426
6427 buffer_handle.update(&mut cx, |buffer, cx| {
6428 buffer.finalize_last_transaction();
6429 buffer.start_transaction();
6430
6431 for (range, text) in edits {
6432 let primary = &completion.old_range;
6433 let start_within = primary.start.cmp(&range.start, buffer).is_le()
6434 && primary.end.cmp(&range.start, buffer).is_ge();
6435 let end_within = range.start.cmp(&primary.end, buffer).is_le()
6436 && range.end.cmp(&primary.end, buffer).is_ge();
6437
6438 //Skip additional edits which overlap with the primary completion edit
6439 //https://github.com/zed-industries/zed/pull/1871
6440 if !start_within && !end_within {
6441 buffer.edit([(range, text)], None, cx);
6442 }
6443 }
6444
6445 let transaction = if buffer.end_transaction(cx).is_some() {
6446 let transaction = buffer.finalize_last_transaction().unwrap().clone();
6447 if !push_to_history {
6448 buffer.forget_transaction(transaction.id);
6449 }
6450 Some(transaction)
6451 } else {
6452 None
6453 };
6454 Ok(transaction)
6455 })?
6456 } else {
6457 Ok(None)
6458 }
6459 })
6460 } else if let Some(project_id) = self.remote_id() {
6461 let client = self.client.clone();
6462 cx.spawn(move |_, mut cx| async move {
6463 let response = client
6464 .request(proto::ApplyCompletionAdditionalEdits {
6465 project_id,
6466 buffer_id: buffer_id.into(),
6467 completion: Some(Self::serialize_completion(&CoreCompletion {
6468 old_range: completion.old_range,
6469 new_text: completion.new_text,
6470 server_id: completion.server_id,
6471 lsp_completion: completion.lsp_completion,
6472 })),
6473 })
6474 .await?;
6475
6476 if let Some(transaction) = response.transaction {
6477 let transaction = language::proto::deserialize_transaction(transaction)?;
6478 buffer_handle
6479 .update(&mut cx, |buffer, _| {
6480 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
6481 })?
6482 .await?;
6483 if push_to_history {
6484 buffer_handle.update(&mut cx, |buffer, _| {
6485 buffer.push_transaction(transaction.clone(), Instant::now());
6486 })?;
6487 }
6488 Ok(Some(transaction))
6489 } else {
6490 Ok(None)
6491 }
6492 })
6493 } else {
6494 Task::ready(Err(anyhow!("project does not have a remote id")))
6495 }
6496 }
6497
6498 fn code_actions_impl(
6499 &mut self,
6500 buffer_handle: &Model<Buffer>,
6501 range: Range<Anchor>,
6502 cx: &mut ModelContext<Self>,
6503 ) -> Task<Vec<CodeAction>> {
6504 if self.is_local_or_ssh() {
6505 let all_actions_task = self.request_multiple_lsp_locally(
6506 &buffer_handle,
6507 Some(range.start),
6508 GetCodeActions {
6509 range: range.clone(),
6510 kinds: None,
6511 },
6512 cx,
6513 );
6514 cx.spawn(|_, _| async move { all_actions_task.await.into_iter().flatten().collect() })
6515 } else if let Some(project_id) = self.remote_id() {
6516 let request_task = self.client().request(proto::MultiLspQuery {
6517 buffer_id: buffer_handle.read(cx).remote_id().into(),
6518 version: serialize_version(&buffer_handle.read(cx).version()),
6519 project_id,
6520 strategy: Some(proto::multi_lsp_query::Strategy::All(
6521 proto::AllLanguageServers {},
6522 )),
6523 request: Some(proto::multi_lsp_query::Request::GetCodeActions(
6524 GetCodeActions {
6525 range: range.clone(),
6526 kinds: None,
6527 }
6528 .to_proto(project_id, buffer_handle.read(cx)),
6529 )),
6530 });
6531 let buffer = buffer_handle.clone();
6532 cx.spawn(|weak_project, cx| async move {
6533 let Some(project) = weak_project.upgrade() else {
6534 return Vec::new();
6535 };
6536 join_all(
6537 request_task
6538 .await
6539 .log_err()
6540 .map(|response| response.responses)
6541 .unwrap_or_default()
6542 .into_iter()
6543 .filter_map(|lsp_response| match lsp_response.response? {
6544 proto::lsp_response::Response::GetCodeActionsResponse(response) => {
6545 Some(response)
6546 }
6547 unexpected => {
6548 debug_panic!("Unexpected response: {unexpected:?}");
6549 None
6550 }
6551 })
6552 .map(|code_actions_response| {
6553 let response = GetCodeActions {
6554 range: range.clone(),
6555 kinds: None,
6556 }
6557 .response_from_proto(
6558 code_actions_response,
6559 project.clone(),
6560 buffer.clone(),
6561 cx.clone(),
6562 );
6563 async move { response.await.log_err().unwrap_or_default() }
6564 }),
6565 )
6566 .await
6567 .into_iter()
6568 .flatten()
6569 .collect()
6570 })
6571 } else {
6572 log::error!("cannot fetch actions: project does not have a remote id");
6573 Task::ready(Vec::new())
6574 }
6575 }
6576
6577 pub fn code_actions<T: Clone + ToOffset>(
6578 &mut self,
6579 buffer_handle: &Model<Buffer>,
6580 range: Range<T>,
6581 cx: &mut ModelContext<Self>,
6582 ) -> Task<Vec<CodeAction>> {
6583 let buffer = buffer_handle.read(cx);
6584 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
6585 self.code_actions_impl(buffer_handle, range, cx)
6586 }
6587
6588 pub fn apply_code_action(
6589 &self,
6590 buffer_handle: Model<Buffer>,
6591 mut action: CodeAction,
6592 push_to_history: bool,
6593 cx: &mut ModelContext<Self>,
6594 ) -> Task<Result<ProjectTransaction>> {
6595 if self.is_local_or_ssh() {
6596 let buffer = buffer_handle.read(cx);
6597 let (lsp_adapter, lang_server) = if let Some((adapter, server)) =
6598 self.language_server_for_buffer(buffer, action.server_id, cx)
6599 {
6600 (adapter.clone(), server.clone())
6601 } else {
6602 return Task::ready(Ok(Default::default()));
6603 };
6604 cx.spawn(move |this, mut cx| async move {
6605 Self::try_resolve_code_action(&lang_server, &mut action)
6606 .await
6607 .context("resolving a code action")?;
6608 if let Some(edit) = action.lsp_action.edit {
6609 if edit.changes.is_some() || edit.document_changes.is_some() {
6610 return Self::deserialize_workspace_edit(
6611 this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
6612 edit,
6613 push_to_history,
6614 lsp_adapter.clone(),
6615 lang_server.clone(),
6616 &mut cx,
6617 )
6618 .await;
6619 }
6620 }
6621
6622 if let Some(command) = action.lsp_action.command {
6623 this.update(&mut cx, |this, _| {
6624 this.last_workspace_edits_by_language_server
6625 .remove(&lang_server.server_id());
6626 })?;
6627
6628 let result = lang_server
6629 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
6630 command: command.command,
6631 arguments: command.arguments.unwrap_or_default(),
6632 ..Default::default()
6633 })
6634 .await;
6635
6636 if let Err(err) = result {
6637 // TODO: LSP ERROR
6638 return Err(err);
6639 }
6640
6641 return this.update(&mut cx, |this, _| {
6642 this.last_workspace_edits_by_language_server
6643 .remove(&lang_server.server_id())
6644 .unwrap_or_default()
6645 });
6646 }
6647
6648 Ok(ProjectTransaction::default())
6649 })
6650 } else if let Some(project_id) = self.remote_id() {
6651 let client = self.client.clone();
6652 let request = proto::ApplyCodeAction {
6653 project_id,
6654 buffer_id: buffer_handle.read(cx).remote_id().into(),
6655 action: Some(Self::serialize_code_action(&action)),
6656 };
6657 cx.spawn(move |this, cx| async move {
6658 let response = client
6659 .request(request)
6660 .await?
6661 .transaction
6662 .ok_or_else(|| anyhow!("missing transaction"))?;
6663 Self::deserialize_project_transaction(this, response, push_to_history, cx).await
6664 })
6665 } else {
6666 Task::ready(Err(anyhow!("project does not have a remote id")))
6667 }
6668 }
6669
6670 fn apply_on_type_formatting(
6671 &self,
6672 buffer: Model<Buffer>,
6673 position: Anchor,
6674 trigger: String,
6675 cx: &mut ModelContext<Self>,
6676 ) -> Task<Result<Option<Transaction>>> {
6677 if self.is_local_or_ssh() {
6678 cx.spawn(move |this, mut cx| async move {
6679 // Do not allow multiple concurrent formatting requests for the
6680 // same buffer.
6681 this.update(&mut cx, |this, cx| {
6682 this.buffers_being_formatted
6683 .insert(buffer.read(cx).remote_id())
6684 })?;
6685
6686 let _cleanup = defer({
6687 let this = this.clone();
6688 let mut cx = cx.clone();
6689 let closure_buffer = buffer.clone();
6690 move || {
6691 this.update(&mut cx, |this, cx| {
6692 this.buffers_being_formatted
6693 .remove(&closure_buffer.read(cx).remote_id());
6694 })
6695 .ok();
6696 }
6697 });
6698
6699 buffer
6700 .update(&mut cx, |buffer, _| {
6701 buffer.wait_for_edits(Some(position.timestamp))
6702 })?
6703 .await?;
6704 this.update(&mut cx, |this, cx| {
6705 let position = position.to_point_utf16(buffer.read(cx));
6706 this.on_type_format(buffer, position, trigger, false, cx)
6707 })?
6708 .await
6709 })
6710 } else if let Some(project_id) = self.remote_id() {
6711 let client = self.client.clone();
6712 let request = proto::OnTypeFormatting {
6713 project_id,
6714 buffer_id: buffer.read(cx).remote_id().into(),
6715 position: Some(serialize_anchor(&position)),
6716 trigger,
6717 version: serialize_version(&buffer.read(cx).version()),
6718 };
6719 cx.spawn(move |_, _| async move {
6720 client
6721 .request(request)
6722 .await?
6723 .transaction
6724 .map(language::proto::deserialize_transaction)
6725 .transpose()
6726 })
6727 } else {
6728 Task::ready(Err(anyhow!("project does not have a remote id")))
6729 }
6730 }
6731
6732 async fn deserialize_edits(
6733 this: Model<Self>,
6734 buffer_to_edit: Model<Buffer>,
6735 edits: Vec<lsp::TextEdit>,
6736 push_to_history: bool,
6737 _: Arc<CachedLspAdapter>,
6738 language_server: Arc<LanguageServer>,
6739 cx: &mut AsyncAppContext,
6740 ) -> Result<Option<Transaction>> {
6741 let edits = this
6742 .update(cx, |this, cx| {
6743 this.edits_from_lsp(
6744 &buffer_to_edit,
6745 edits,
6746 language_server.server_id(),
6747 None,
6748 cx,
6749 )
6750 })?
6751 .await?;
6752
6753 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
6754 buffer.finalize_last_transaction();
6755 buffer.start_transaction();
6756 for (range, text) in edits {
6757 buffer.edit([(range, text)], None, cx);
6758 }
6759
6760 if buffer.end_transaction(cx).is_some() {
6761 let transaction = buffer.finalize_last_transaction().unwrap().clone();
6762 if !push_to_history {
6763 buffer.forget_transaction(transaction.id);
6764 }
6765 Some(transaction)
6766 } else {
6767 None
6768 }
6769 })?;
6770
6771 Ok(transaction)
6772 }
6773
6774 async fn deserialize_workspace_edit(
6775 this: Model<Self>,
6776 edit: lsp::WorkspaceEdit,
6777 push_to_history: bool,
6778 lsp_adapter: Arc<CachedLspAdapter>,
6779 language_server: Arc<LanguageServer>,
6780 cx: &mut AsyncAppContext,
6781 ) -> Result<ProjectTransaction> {
6782 let fs = this.update(cx, |this, _| this.fs.clone())?;
6783 let mut operations = Vec::new();
6784 if let Some(document_changes) = edit.document_changes {
6785 match document_changes {
6786 lsp::DocumentChanges::Edits(edits) => {
6787 operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
6788 }
6789 lsp::DocumentChanges::Operations(ops) => operations = ops,
6790 }
6791 } else if let Some(changes) = edit.changes {
6792 operations.extend(changes.into_iter().map(|(uri, edits)| {
6793 lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
6794 text_document: lsp::OptionalVersionedTextDocumentIdentifier {
6795 uri,
6796 version: None,
6797 },
6798 edits: edits.into_iter().map(Edit::Plain).collect(),
6799 })
6800 }));
6801 }
6802
6803 let mut project_transaction = ProjectTransaction::default();
6804 for operation in operations {
6805 match operation {
6806 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
6807 let abs_path = op
6808 .uri
6809 .to_file_path()
6810 .map_err(|_| anyhow!("can't convert URI to path"))?;
6811
6812 if let Some(parent_path) = abs_path.parent() {
6813 fs.create_dir(parent_path).await?;
6814 }
6815 if abs_path.ends_with("/") {
6816 fs.create_dir(&abs_path).await?;
6817 } else {
6818 fs.create_file(
6819 &abs_path,
6820 op.options
6821 .map(|options| fs::CreateOptions {
6822 overwrite: options.overwrite.unwrap_or(false),
6823 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
6824 })
6825 .unwrap_or_default(),
6826 )
6827 .await?;
6828 }
6829 }
6830
6831 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
6832 let source_abs_path = op
6833 .old_uri
6834 .to_file_path()
6835 .map_err(|_| anyhow!("can't convert URI to path"))?;
6836 let target_abs_path = op
6837 .new_uri
6838 .to_file_path()
6839 .map_err(|_| anyhow!("can't convert URI to path"))?;
6840 fs.rename(
6841 &source_abs_path,
6842 &target_abs_path,
6843 op.options
6844 .map(|options| fs::RenameOptions {
6845 overwrite: options.overwrite.unwrap_or(false),
6846 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
6847 })
6848 .unwrap_or_default(),
6849 )
6850 .await?;
6851 }
6852
6853 lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
6854 let abs_path = op
6855 .uri
6856 .to_file_path()
6857 .map_err(|_| anyhow!("can't convert URI to path"))?;
6858 let options = op
6859 .options
6860 .map(|options| fs::RemoveOptions {
6861 recursive: options.recursive.unwrap_or(false),
6862 ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
6863 })
6864 .unwrap_or_default();
6865 if abs_path.ends_with("/") {
6866 fs.remove_dir(&abs_path, options).await?;
6867 } else {
6868 fs.remove_file(&abs_path, options).await?;
6869 }
6870 }
6871
6872 lsp::DocumentChangeOperation::Edit(op) => {
6873 let buffer_to_edit = this
6874 .update(cx, |this, cx| {
6875 this.open_local_buffer_via_lsp(
6876 op.text_document.uri.clone(),
6877 language_server.server_id(),
6878 lsp_adapter.name.clone(),
6879 cx,
6880 )
6881 })?
6882 .await?;
6883
6884 let edits = this
6885 .update(cx, |this, cx| {
6886 let path = buffer_to_edit.read(cx).project_path(cx);
6887 let active_entry = this.active_entry;
6888 let is_active_entry = path.clone().map_or(false, |project_path| {
6889 this.entry_for_path(&project_path, cx)
6890 .map_or(false, |entry| Some(entry.id) == active_entry)
6891 });
6892
6893 let (mut edits, mut snippet_edits) = (vec![], vec![]);
6894 for edit in op.edits {
6895 match edit {
6896 Edit::Plain(edit) => edits.push(edit),
6897 Edit::Annotated(edit) => edits.push(edit.text_edit),
6898 Edit::Snippet(edit) => {
6899 let Ok(snippet) = Snippet::parse(&edit.snippet.value)
6900 else {
6901 continue;
6902 };
6903
6904 if is_active_entry {
6905 snippet_edits.push((edit.range, snippet));
6906 } else {
6907 // Since this buffer is not focused, apply a normal edit.
6908 edits.push(TextEdit {
6909 range: edit.range,
6910 new_text: snippet.text,
6911 });
6912 }
6913 }
6914 }
6915 }
6916 if !snippet_edits.is_empty() {
6917 if let Some(buffer_version) = op.text_document.version {
6918 let buffer_id = buffer_to_edit.read(cx).remote_id();
6919 // Check if the edit that triggered that edit has been made by this participant.
6920 let should_apply_edit = this
6921 .buffer_snapshots
6922 .get(&buffer_id)
6923 .and_then(|server_to_snapshots| {
6924 let all_snapshots = server_to_snapshots
6925 .get(&language_server.server_id())?;
6926 all_snapshots
6927 .binary_search_by_key(&buffer_version, |snapshot| {
6928 snapshot.version
6929 })
6930 .ok()
6931 .and_then(|index| all_snapshots.get(index))
6932 })
6933 .map_or(false, |lsp_snapshot| {
6934 let version = lsp_snapshot.snapshot.version();
6935 let most_recent_edit = version
6936 .iter()
6937 .max_by_key(|timestamp| timestamp.value);
6938 most_recent_edit.map_or(false, |edit| {
6939 edit.replica_id == this.replica_id()
6940 })
6941 });
6942 if should_apply_edit {
6943 cx.emit(Event::SnippetEdit(buffer_id, snippet_edits));
6944 }
6945 }
6946 }
6947
6948 this.edits_from_lsp(
6949 &buffer_to_edit,
6950 edits,
6951 language_server.server_id(),
6952 op.text_document.version,
6953 cx,
6954 )
6955 })?
6956 .await?;
6957
6958 let transaction = buffer_to_edit.update(cx, |buffer, cx| {
6959 buffer.finalize_last_transaction();
6960 buffer.start_transaction();
6961 for (range, text) in edits {
6962 buffer.edit([(range, text)], None, cx);
6963 }
6964 let transaction = if buffer.end_transaction(cx).is_some() {
6965 let transaction = buffer.finalize_last_transaction().unwrap().clone();
6966 if !push_to_history {
6967 buffer.forget_transaction(transaction.id);
6968 }
6969 Some(transaction)
6970 } else {
6971 None
6972 };
6973
6974 transaction
6975 })?;
6976 if let Some(transaction) = transaction {
6977 project_transaction.0.insert(buffer_to_edit, transaction);
6978 }
6979 }
6980 }
6981 }
6982
6983 Ok(project_transaction)
6984 }
6985
6986 fn prepare_rename_impl(
6987 &mut self,
6988 buffer: Model<Buffer>,
6989 position: PointUtf16,
6990 cx: &mut ModelContext<Self>,
6991 ) -> Task<Result<Option<Range<Anchor>>>> {
6992 self.request_lsp(
6993 buffer,
6994 LanguageServerToQuery::Primary,
6995 PrepareRename { position },
6996 cx,
6997 )
6998 }
6999 pub fn prepare_rename<T: ToPointUtf16>(
7000 &mut self,
7001 buffer: Model<Buffer>,
7002 position: T,
7003 cx: &mut ModelContext<Self>,
7004 ) -> Task<Result<Option<Range<Anchor>>>> {
7005 let position = position.to_point_utf16(buffer.read(cx));
7006 self.prepare_rename_impl(buffer, position, cx)
7007 }
7008
7009 fn perform_rename_impl(
7010 &mut self,
7011 buffer: Model<Buffer>,
7012 position: PointUtf16,
7013 new_name: String,
7014 push_to_history: bool,
7015 cx: &mut ModelContext<Self>,
7016 ) -> Task<Result<ProjectTransaction>> {
7017 let position = position.to_point_utf16(buffer.read(cx));
7018 self.request_lsp(
7019 buffer,
7020 LanguageServerToQuery::Primary,
7021 PerformRename {
7022 position,
7023 new_name,
7024 push_to_history,
7025 },
7026 cx,
7027 )
7028 }
7029 pub fn perform_rename<T: ToPointUtf16>(
7030 &mut self,
7031 buffer: Model<Buffer>,
7032 position: T,
7033 new_name: String,
7034 push_to_history: bool,
7035 cx: &mut ModelContext<Self>,
7036 ) -> Task<Result<ProjectTransaction>> {
7037 let position = position.to_point_utf16(buffer.read(cx));
7038 self.perform_rename_impl(buffer, position, new_name, push_to_history, cx)
7039 }
7040
7041 pub fn on_type_format_impl(
7042 &mut self,
7043 buffer: Model<Buffer>,
7044 position: PointUtf16,
7045 trigger: String,
7046 push_to_history: bool,
7047 cx: &mut ModelContext<Self>,
7048 ) -> Task<Result<Option<Transaction>>> {
7049 let options = buffer.update(cx, |buffer, cx| {
7050 lsp_command::lsp_formatting_options(language_settings(
7051 buffer.language_at(position).as_ref(),
7052 buffer.file(),
7053 cx,
7054 ))
7055 });
7056 self.request_lsp(
7057 buffer.clone(),
7058 LanguageServerToQuery::Primary,
7059 OnTypeFormatting {
7060 position,
7061 trigger,
7062 options,
7063 push_to_history,
7064 },
7065 cx,
7066 )
7067 }
7068
7069 pub fn on_type_format<T: ToPointUtf16>(
7070 &mut self,
7071 buffer: Model<Buffer>,
7072 position: T,
7073 trigger: String,
7074 push_to_history: bool,
7075 cx: &mut ModelContext<Self>,
7076 ) -> Task<Result<Option<Transaction>>> {
7077 let position = position.to_point_utf16(buffer.read(cx));
7078 self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
7079 }
7080
7081 pub fn inlay_hints<T: ToOffset>(
7082 &mut self,
7083 buffer_handle: Model<Buffer>,
7084 range: Range<T>,
7085 cx: &mut ModelContext<Self>,
7086 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
7087 let buffer = buffer_handle.read(cx);
7088 let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
7089 self.inlay_hints_impl(buffer_handle, range, cx)
7090 }
7091 fn inlay_hints_impl(
7092 &mut self,
7093 buffer_handle: Model<Buffer>,
7094 range: Range<Anchor>,
7095 cx: &mut ModelContext<Self>,
7096 ) -> Task<anyhow::Result<Vec<InlayHint>>> {
7097 let buffer = buffer_handle.read(cx);
7098 let range_start = range.start;
7099 let range_end = range.end;
7100 let buffer_id = buffer.remote_id().into();
7101 let lsp_request = InlayHints { range };
7102
7103 if self.is_local_or_ssh() {
7104 let lsp_request_task = self.request_lsp(
7105 buffer_handle.clone(),
7106 LanguageServerToQuery::Primary,
7107 lsp_request,
7108 cx,
7109 );
7110 cx.spawn(move |_, mut cx| async move {
7111 buffer_handle
7112 .update(&mut cx, |buffer, _| {
7113 buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
7114 })?
7115 .await
7116 .context("waiting for inlay hint request range edits")?;
7117 lsp_request_task.await.context("inlay hints LSP request")
7118 })
7119 } else if let Some(project_id) = self.remote_id() {
7120 let client = self.client.clone();
7121 let request = proto::InlayHints {
7122 project_id,
7123 buffer_id,
7124 start: Some(serialize_anchor(&range_start)),
7125 end: Some(serialize_anchor(&range_end)),
7126 version: serialize_version(&buffer_handle.read(cx).version()),
7127 };
7128 cx.spawn(move |project, cx| async move {
7129 let response = client
7130 .request(request)
7131 .await
7132 .context("inlay hints proto request")?;
7133 LspCommand::response_from_proto(
7134 lsp_request,
7135 response,
7136 project.upgrade().ok_or_else(|| anyhow!("No project"))?,
7137 buffer_handle.clone(),
7138 cx.clone(),
7139 )
7140 .await
7141 .context("inlay hints proto response conversion")
7142 })
7143 } else {
7144 Task::ready(Err(anyhow!("project does not have a remote id")))
7145 }
7146 }
7147
7148 pub fn resolve_inlay_hint(
7149 &self,
7150 hint: InlayHint,
7151 buffer_handle: Model<Buffer>,
7152 server_id: LanguageServerId,
7153 cx: &mut ModelContext<Self>,
7154 ) -> Task<anyhow::Result<InlayHint>> {
7155 if self.is_local_or_ssh() {
7156 let buffer = buffer_handle.read(cx);
7157 let (_, lang_server) = if let Some((adapter, server)) =
7158 self.language_server_for_buffer(buffer, server_id, cx)
7159 {
7160 (adapter.clone(), server.clone())
7161 } else {
7162 return Task::ready(Ok(hint));
7163 };
7164 if !InlayHints::can_resolve_inlays(&lang_server.capabilities()) {
7165 return Task::ready(Ok(hint));
7166 }
7167
7168 let buffer_snapshot = buffer.snapshot();
7169 cx.spawn(move |_, mut cx| async move {
7170 let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
7171 InlayHints::project_to_lsp_hint(hint, &buffer_snapshot),
7172 );
7173 let resolved_hint = resolve_task
7174 .await
7175 .context("inlay hint resolve LSP request")?;
7176 let resolved_hint = InlayHints::lsp_to_project_hint(
7177 resolved_hint,
7178 &buffer_handle,
7179 server_id,
7180 ResolveState::Resolved,
7181 false,
7182 &mut cx,
7183 )
7184 .await?;
7185 Ok(resolved_hint)
7186 })
7187 } else if let Some(project_id) = self.remote_id() {
7188 let client = self.client.clone();
7189 let request = proto::ResolveInlayHint {
7190 project_id,
7191 buffer_id: buffer_handle.read(cx).remote_id().into(),
7192 language_server_id: server_id.0 as u64,
7193 hint: Some(InlayHints::project_to_proto_hint(hint.clone())),
7194 };
7195 cx.spawn(move |_, _| async move {
7196 let response = client
7197 .request(request)
7198 .await
7199 .context("inlay hints proto request")?;
7200 match response.hint {
7201 Some(resolved_hint) => InlayHints::proto_to_project_hint(resolved_hint)
7202 .context("inlay hints proto resolve response conversion"),
7203 None => Ok(hint),
7204 }
7205 })
7206 } else {
7207 Task::ready(Err(anyhow!("project does not have a remote id")))
7208 }
7209 }
7210
7211 pub fn search(
7212 &mut self,
7213 query: SearchQuery,
7214 cx: &mut ModelContext<Self>,
7215 ) -> Receiver<SearchResult> {
7216 let (result_tx, result_rx) = smol::channel::unbounded();
7217
7218 let matching_buffers_rx =
7219 self.search_for_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx);
7220
7221 cx.spawn(|_, cx| async move {
7222 let mut range_count = 0;
7223 let mut buffer_count = 0;
7224 let mut limit_reached = false;
7225 let query = Arc::new(query);
7226 let mut chunks = matching_buffers_rx.ready_chunks(64);
7227
7228 // Now that we know what paths match the query, we will load at most
7229 // 64 buffers at a time to avoid overwhelming the main thread. For each
7230 // opened buffer, we will spawn a background task that retrieves all the
7231 // ranges in the buffer matched by the query.
7232 'outer: while let Some(matching_buffer_chunk) = chunks.next().await {
7233 let mut chunk_results = Vec::new();
7234 for buffer in matching_buffer_chunk {
7235 let buffer = buffer.clone();
7236 let query = query.clone();
7237 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
7238 chunk_results.push(cx.background_executor().spawn(async move {
7239 let ranges = query
7240 .search(&snapshot, None)
7241 .await
7242 .iter()
7243 .map(|range| {
7244 snapshot.anchor_before(range.start)
7245 ..snapshot.anchor_after(range.end)
7246 })
7247 .collect::<Vec<_>>();
7248 anyhow::Ok((buffer, ranges))
7249 }));
7250 }
7251
7252 let chunk_results = futures::future::join_all(chunk_results).await;
7253 for result in chunk_results {
7254 if let Some((buffer, ranges)) = result.log_err() {
7255 range_count += ranges.len();
7256 buffer_count += 1;
7257 result_tx
7258 .send(SearchResult::Buffer { buffer, ranges })
7259 .await?;
7260 if buffer_count > MAX_SEARCH_RESULT_FILES
7261 || range_count > MAX_SEARCH_RESULT_RANGES
7262 {
7263 limit_reached = true;
7264 break 'outer;
7265 }
7266 }
7267 }
7268 }
7269
7270 if limit_reached {
7271 result_tx.send(SearchResult::LimitReached).await?;
7272 }
7273
7274 anyhow::Ok(())
7275 })
7276 .detach();
7277
7278 result_rx
7279 }
7280
7281 fn search_for_candidate_buffers(
7282 &mut self,
7283 query: &SearchQuery,
7284 limit: usize,
7285 cx: &mut ModelContext<Project>,
7286 ) -> Receiver<Model<Buffer>> {
7287 if self.is_local() {
7288 let fs = self.fs.clone();
7289 return self.buffer_store.update(cx, |buffer_store, cx| {
7290 buffer_store.find_search_candidates(query, limit, fs, cx)
7291 });
7292 } else {
7293 self.search_for_candidate_buffers_remote(query, limit, cx)
7294 }
7295 }
7296
7297 fn search_for_candidate_buffers_remote(
7298 &mut self,
7299 query: &SearchQuery,
7300 limit: usize,
7301 cx: &mut ModelContext<Project>,
7302 ) -> Receiver<Model<Buffer>> {
7303 let (tx, rx) = smol::channel::unbounded();
7304
7305 let (client, remote_id): (AnyProtoClient, _) =
7306 if let Some(ssh_session) = self.ssh_session.clone() {
7307 (ssh_session.into(), 0)
7308 } else if let Some(remote_id) = self.remote_id() {
7309 (self.client.clone().into(), remote_id)
7310 } else {
7311 return rx;
7312 };
7313
7314 let request = client.request(proto::FindSearchCandidates {
7315 project_id: remote_id,
7316 query: Some(query.to_proto()),
7317 limit: limit as _,
7318 });
7319 let guard = self.retain_remotely_created_buffers();
7320
7321 cx.spawn(move |this, mut cx| async move {
7322 let response = request.await?;
7323 for buffer_id in response.buffer_ids {
7324 let buffer_id = BufferId::new(buffer_id)?;
7325 let buffer = this
7326 .update(&mut cx, |this, cx| {
7327 this.wait_for_remote_buffer(buffer_id, cx)
7328 })?
7329 .await?;
7330 let _ = tx.send(buffer).await;
7331 }
7332
7333 drop(guard);
7334 anyhow::Ok(())
7335 })
7336 .detach_and_log_err(cx);
7337 rx
7338 }
7339
7340 pub fn request_lsp<R: LspCommand>(
7341 &self,
7342 buffer_handle: Model<Buffer>,
7343 server: LanguageServerToQuery,
7344 request: R,
7345 cx: &mut ModelContext<Self>,
7346 ) -> Task<Result<R::Response>>
7347 where
7348 <R::LspRequest as lsp::request::Request>::Result: Send,
7349 <R::LspRequest as lsp::request::Request>::Params: Send,
7350 {
7351 let buffer = buffer_handle.read(cx);
7352 if self.is_local_or_ssh() {
7353 let language_server = match server {
7354 LanguageServerToQuery::Primary => {
7355 match self.primary_language_server_for_buffer(buffer, cx) {
7356 Some((_, server)) => Some(Arc::clone(server)),
7357 None => return Task::ready(Ok(Default::default())),
7358 }
7359 }
7360 LanguageServerToQuery::Other(id) => self
7361 .language_server_for_buffer(buffer, id, cx)
7362 .map(|(_, server)| Arc::clone(server)),
7363 };
7364 let file = File::from_dyn(buffer.file()).and_then(File::as_local);
7365 if let (Some(file), Some(language_server)) = (file, language_server) {
7366 let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx);
7367 let status = request.status();
7368 return cx.spawn(move |this, cx| async move {
7369 if !request.check_capabilities(language_server.adapter_server_capabilities()) {
7370 return Ok(Default::default());
7371 }
7372
7373 let lsp_request = language_server.request::<R::LspRequest>(lsp_params);
7374
7375 let id = lsp_request.id();
7376 let _cleanup = if status.is_some() {
7377 cx.update(|cx| {
7378 this.update(cx, |this, cx| {
7379 this.on_lsp_work_start(
7380 language_server.server_id(),
7381 id.to_string(),
7382 LanguageServerProgress {
7383 is_disk_based_diagnostics_progress: false,
7384 is_cancellable: false,
7385 title: None,
7386 message: status.clone(),
7387 percentage: None,
7388 last_update_at: cx.background_executor().now(),
7389 },
7390 cx,
7391 );
7392 })
7393 })
7394 .log_err();
7395
7396 Some(defer(|| {
7397 cx.update(|cx| {
7398 this.update(cx, |this, cx| {
7399 this.on_lsp_work_end(
7400 language_server.server_id(),
7401 id.to_string(),
7402 cx,
7403 );
7404 })
7405 })
7406 .log_err();
7407 }))
7408 } else {
7409 None
7410 };
7411
7412 let result = lsp_request.await;
7413
7414 let response = result.map_err(|err| {
7415 log::warn!(
7416 "Generic lsp request to {} failed: {}",
7417 language_server.name(),
7418 err
7419 );
7420 err
7421 })?;
7422
7423 request
7424 .response_from_lsp(
7425 response,
7426 this.upgrade().ok_or_else(|| anyhow!("no app context"))?,
7427 buffer_handle,
7428 language_server.server_id(),
7429 cx.clone(),
7430 )
7431 .await
7432 });
7433 }
7434 } else if let Some(project_id) = self.remote_id() {
7435 return self.send_lsp_proto_request(buffer_handle, project_id, request, cx);
7436 }
7437
7438 Task::ready(Ok(Default::default()))
7439 }
7440
7441 fn request_multiple_lsp_locally<P, R>(
7442 &self,
7443 buffer: &Model<Buffer>,
7444 position: Option<P>,
7445 request: R,
7446 cx: &mut ModelContext<'_, Self>,
7447 ) -> Task<Vec<R::Response>>
7448 where
7449 P: ToOffset,
7450 R: LspCommand + Clone,
7451 <R::LspRequest as lsp::request::Request>::Result: Send,
7452 <R::LspRequest as lsp::request::Request>::Params: Send,
7453 {
7454 if !self.is_local_or_ssh() {
7455 debug_panic!("Should not request multiple lsp commands in non-local project");
7456 return Task::ready(Vec::new());
7457 }
7458 let snapshot = buffer.read(cx).snapshot();
7459 let scope = position.and_then(|position| snapshot.language_scope_at(position));
7460 let mut response_results = self
7461 .language_servers_for_buffer(buffer.read(cx), cx)
7462 .filter(|(adapter, _)| {
7463 scope
7464 .as_ref()
7465 .map(|scope| scope.language_allowed(&adapter.name))
7466 .unwrap_or(true)
7467 })
7468 .map(|(_, server)| server.server_id())
7469 .map(|server_id| {
7470 self.request_lsp(
7471 buffer.clone(),
7472 LanguageServerToQuery::Other(server_id),
7473 request.clone(),
7474 cx,
7475 )
7476 })
7477 .collect::<FuturesUnordered<_>>();
7478
7479 return cx.spawn(|_, _| async move {
7480 let mut responses = Vec::with_capacity(response_results.len());
7481 while let Some(response_result) = response_results.next().await {
7482 if let Some(response) = response_result.log_err() {
7483 responses.push(response);
7484 }
7485 }
7486 responses
7487 });
7488 }
7489
7490 fn send_lsp_proto_request<R: LspCommand>(
7491 &self,
7492 buffer: Model<Buffer>,
7493 project_id: u64,
7494 request: R,
7495 cx: &mut ModelContext<'_, Project>,
7496 ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
7497 let rpc = self.client.clone();
7498 let message = request.to_proto(project_id, buffer.read(cx));
7499 cx.spawn(move |this, mut cx| async move {
7500 // Ensure the project is still alive by the time the task
7501 // is scheduled.
7502 this.upgrade().context("project dropped")?;
7503 let response = rpc.request(message).await?;
7504 let this = this.upgrade().context("project dropped")?;
7505 if this.update(&mut cx, |this, _| this.is_disconnected())? {
7506 Err(anyhow!("disconnected before completing request"))
7507 } else {
7508 request
7509 .response_from_proto(response, this, buffer, cx)
7510 .await
7511 }
7512 })
7513 }
7514
7515 /// Move a worktree to a new position in the worktree order.
7516 ///
7517 /// The worktree will moved to the opposite side of the destination worktree.
7518 ///
7519 /// # Example
7520 ///
7521 /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `33`,
7522 /// worktree_order will be updated to produce the indexes `[11, 33, 22]`.
7523 ///
7524 /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `11`,
7525 /// worktree_order will be updated to produce the indexes `[22, 11, 33]`.
7526 ///
7527 /// # Errors
7528 ///
7529 /// An error will be returned if the worktree or destination worktree are not found.
7530 pub fn move_worktree(
7531 &mut self,
7532 source: WorktreeId,
7533 destination: WorktreeId,
7534 cx: &mut ModelContext<'_, Self>,
7535 ) -> Result<()> {
7536 self.worktree_store.update(cx, |worktree_store, cx| {
7537 worktree_store.move_worktree(source, destination, cx)
7538 })
7539 }
7540
7541 pub fn find_or_create_worktree(
7542 &mut self,
7543 abs_path: impl AsRef<Path>,
7544 visible: bool,
7545 cx: &mut ModelContext<Self>,
7546 ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
7547 let abs_path = abs_path.as_ref();
7548 if let Some((tree, relative_path)) = self.find_worktree(abs_path, cx) {
7549 Task::ready(Ok((tree, relative_path)))
7550 } else {
7551 let worktree = self.create_worktree(abs_path, visible, cx);
7552 cx.background_executor()
7553 .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
7554 }
7555 }
7556
7557 pub fn find_worktree(
7558 &self,
7559 abs_path: &Path,
7560 cx: &AppContext,
7561 ) -> Option<(Model<Worktree>, PathBuf)> {
7562 self.worktree_store.read_with(cx, |worktree_store, cx| {
7563 for tree in worktree_store.worktrees() {
7564 if let Ok(relative_path) = abs_path.strip_prefix(tree.read(cx).abs_path()) {
7565 return Some((tree.clone(), relative_path.into()));
7566 }
7567 }
7568 None
7569 })
7570 }
7571
7572 pub fn is_shared(&self) -> bool {
7573 match &self.client_state {
7574 ProjectClientState::Shared { .. } => true,
7575 ProjectClientState::Local => false,
7576 ProjectClientState::Remote { in_room, .. } => *in_room,
7577 }
7578 }
7579
7580 // Returns the resolved version of `path`, that was found in `buffer`, if it exists.
7581 pub fn resolve_existing_file_path(
7582 &self,
7583 path: &str,
7584 buffer: &Model<Buffer>,
7585 cx: &mut ModelContext<Self>,
7586 ) -> Task<Option<ResolvedPath>> {
7587 // TODO: ssh based remoting.
7588 if self.ssh_session.is_some() {
7589 return Task::ready(None);
7590 }
7591
7592 if self.is_local_or_ssh() {
7593 let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
7594
7595 if expanded.is_absolute() {
7596 let fs = self.fs.clone();
7597 cx.background_executor().spawn(async move {
7598 let path = expanded.as_path();
7599 let exists = fs.is_file(path).await;
7600
7601 exists.then(|| ResolvedPath::AbsPath(expanded))
7602 })
7603 } else {
7604 self.resolve_path_in_worktrees(expanded, buffer, cx)
7605 }
7606 } else {
7607 let path = PathBuf::from(path);
7608 if path.is_absolute() || path.starts_with("~") {
7609 return Task::ready(None);
7610 }
7611
7612 self.resolve_path_in_worktrees(path, buffer, cx)
7613 }
7614 }
7615
7616 fn resolve_path_in_worktrees(
7617 &self,
7618 path: PathBuf,
7619 buffer: &Model<Buffer>,
7620 cx: &mut ModelContext<Self>,
7621 ) -> Task<Option<ResolvedPath>> {
7622 let mut candidates = vec![path.clone()];
7623
7624 if let Some(file) = buffer.read(cx).file() {
7625 if let Some(dir) = file.path().parent() {
7626 let joined = dir.to_path_buf().join(path);
7627 candidates.push(joined);
7628 }
7629 }
7630
7631 let worktrees = self.worktrees(cx).collect::<Vec<_>>();
7632 cx.spawn(|_, mut cx| async move {
7633 for worktree in worktrees {
7634 for candidate in candidates.iter() {
7635 let path = worktree
7636 .update(&mut cx, |worktree, _| {
7637 let root_entry_path = &worktree.root_entry()?.path;
7638
7639 let resolved = resolve_path(&root_entry_path, candidate);
7640
7641 let stripped =
7642 resolved.strip_prefix(&root_entry_path).unwrap_or(&resolved);
7643
7644 worktree.entry_for_path(stripped).map(|entry| {
7645 ResolvedPath::ProjectPath(ProjectPath {
7646 worktree_id: worktree.id(),
7647 path: entry.path.clone(),
7648 })
7649 })
7650 })
7651 .ok()?;
7652
7653 if path.is_some() {
7654 return path;
7655 }
7656 }
7657 }
7658 None
7659 })
7660 }
7661
7662 pub fn list_directory(
7663 &self,
7664 query: String,
7665 cx: &mut ModelContext<Self>,
7666 ) -> Task<Result<Vec<PathBuf>>> {
7667 if self.is_local_or_ssh() {
7668 DirectoryLister::Local(self.fs.clone()).list_directory(query, cx)
7669 } else if let Some(dev_server) = self.dev_server_project_id().and_then(|id| {
7670 dev_server_projects::Store::global(cx)
7671 .read(cx)
7672 .dev_server_for_project(id)
7673 }) {
7674 let request = proto::ListRemoteDirectory {
7675 dev_server_id: dev_server.id.0,
7676 path: query,
7677 };
7678 let response = self.client.request(request);
7679 cx.background_executor().spawn(async move {
7680 let response = response.await?;
7681 Ok(response.entries.into_iter().map(PathBuf::from).collect())
7682 })
7683 } else {
7684 Task::ready(Err(anyhow!("cannot list directory in remote project")))
7685 }
7686 }
7687
7688 fn create_worktree(
7689 &mut self,
7690 abs_path: impl AsRef<Path>,
7691 visible: bool,
7692 cx: &mut ModelContext<Self>,
7693 ) -> Task<Result<Model<Worktree>>> {
7694 let path: Arc<Path> = abs_path.as_ref().into();
7695 if !self.loading_worktrees.contains_key(&path) {
7696 let task = if self.ssh_session.is_some() {
7697 self.create_ssh_worktree(abs_path, visible, cx)
7698 } else if self.is_local_or_ssh() {
7699 self.create_local_worktree(abs_path, visible, cx)
7700 } else if self.dev_server_project_id.is_some() {
7701 self.create_dev_server_worktree(abs_path, cx)
7702 } else {
7703 return Task::ready(Err(anyhow!("not a local project")));
7704 };
7705 self.loading_worktrees.insert(path.clone(), task.shared());
7706 }
7707 let task = self.loading_worktrees.get(&path).unwrap().clone();
7708 cx.background_executor().spawn(async move {
7709 let result = match task.await {
7710 Ok(worktree) => Ok(worktree),
7711 Err(err) => Err(anyhow!("{}", err)),
7712 };
7713 result
7714 })
7715 }
7716
7717 fn create_ssh_worktree(
7718 &mut self,
7719 abs_path: impl AsRef<Path>,
7720 visible: bool,
7721 cx: &mut ModelContext<Self>,
7722 ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
7723 let ssh = self.ssh_session.clone().unwrap();
7724 let abs_path = abs_path.as_ref();
7725 let root_name = abs_path.file_name().unwrap().to_string_lossy().to_string();
7726 let path = abs_path.to_string_lossy().to_string();
7727 cx.spawn(|this, mut cx| async move {
7728 let response = ssh.request(AddWorktree { path: path.clone() }).await?;
7729 let worktree = cx.update(|cx| {
7730 Worktree::remote(
7731 0,
7732 0,
7733 proto::WorktreeMetadata {
7734 id: response.worktree_id,
7735 root_name,
7736 visible,
7737 abs_path: path,
7738 },
7739 ssh.clone().into(),
7740 cx,
7741 )
7742 })?;
7743
7744 this.update(&mut cx, |this, cx| this.add_worktree(&worktree, cx))?;
7745
7746 Ok(worktree)
7747 })
7748 }
7749
7750 fn create_local_worktree(
7751 &mut self,
7752 abs_path: impl AsRef<Path>,
7753 visible: bool,
7754 cx: &mut ModelContext<Self>,
7755 ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
7756 let fs = self.fs.clone();
7757 let next_entry_id = self.next_entry_id.clone();
7758 let path: Arc<Path> = abs_path.as_ref().into();
7759
7760 cx.spawn(move |project, mut cx| async move {
7761 let worktree = Worktree::local(path.clone(), visible, fs, next_entry_id, &mut cx).await;
7762
7763 project.update(&mut cx, |project, _| {
7764 project.loading_worktrees.remove(&path);
7765 })?;
7766
7767 let worktree = worktree?;
7768 project.update(&mut cx, |project, cx| project.add_worktree(&worktree, cx))?;
7769
7770 if visible {
7771 cx.update(|cx| {
7772 cx.add_recent_document(&path);
7773 })
7774 .log_err();
7775 }
7776
7777 Ok(worktree)
7778 })
7779 }
7780
7781 fn create_dev_server_worktree(
7782 &mut self,
7783 abs_path: impl AsRef<Path>,
7784 cx: &mut ModelContext<Self>,
7785 ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
7786 let client = self.client.clone();
7787 let path: Arc<Path> = abs_path.as_ref().into();
7788 let mut paths: Vec<String> = self
7789 .visible_worktrees(cx)
7790 .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string())
7791 .collect();
7792 paths.push(path.to_string_lossy().to_string());
7793 let request = client.request(proto::UpdateDevServerProject {
7794 dev_server_project_id: self.dev_server_project_id.unwrap().0,
7795 paths,
7796 });
7797
7798 let abs_path = abs_path.as_ref().to_path_buf();
7799 cx.spawn(move |project, mut cx| async move {
7800 let (tx, rx) = futures::channel::oneshot::channel();
7801 let tx = RefCell::new(Some(tx));
7802 let Some(project) = project.upgrade() else {
7803 return Err(anyhow!("project dropped"))?;
7804 };
7805 let observer = cx.update(|cx| {
7806 cx.observe(&project, move |project, cx| {
7807 let abs_path = abs_path.clone();
7808 project.update(cx, |project, cx| {
7809 if let Some((worktree, _)) = project.find_worktree(&abs_path, cx) {
7810 if let Some(tx) = tx.borrow_mut().take() {
7811 tx.send(worktree).ok();
7812 }
7813 }
7814 })
7815 })
7816 })?;
7817
7818 request.await?;
7819 let worktree = rx.await.map_err(|e| anyhow!(e))?;
7820 drop(observer);
7821 project.update(&mut cx, |project, _| {
7822 project.loading_worktrees.remove(&path);
7823 })?;
7824 Ok(worktree)
7825 })
7826 }
7827
7828 pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
7829 if let Some(dev_server_project_id) = self.dev_server_project_id {
7830 let paths: Vec<String> = self
7831 .visible_worktrees(cx)
7832 .filter_map(|worktree| {
7833 if worktree.read(cx).id() == id_to_remove {
7834 None
7835 } else {
7836 Some(worktree.read(cx).abs_path().to_string_lossy().to_string())
7837 }
7838 })
7839 .collect();
7840 if paths.len() > 0 {
7841 let request = self.client.request(proto::UpdateDevServerProject {
7842 dev_server_project_id: dev_server_project_id.0,
7843 paths,
7844 });
7845 cx.background_executor()
7846 .spawn(request)
7847 .detach_and_log_err(cx);
7848 }
7849 return;
7850 }
7851 self.diagnostics.remove(&id_to_remove);
7852 self.diagnostic_summaries.remove(&id_to_remove);
7853 self.cached_shell_environments.remove(&id_to_remove);
7854
7855 let mut servers_to_remove = HashMap::default();
7856 let mut servers_to_preserve = HashSet::default();
7857 for ((worktree_id, server_name), &server_id) in &self.language_server_ids {
7858 if worktree_id == &id_to_remove {
7859 servers_to_remove.insert(server_id, server_name.clone());
7860 } else {
7861 servers_to_preserve.insert(server_id);
7862 }
7863 }
7864 servers_to_remove.retain(|server_id, _| !servers_to_preserve.contains(server_id));
7865 for (server_id_to_remove, server_name) in servers_to_remove {
7866 self.language_server_ids
7867 .remove(&(id_to_remove, server_name));
7868 self.language_server_statuses.remove(&server_id_to_remove);
7869 self.language_server_watched_paths
7870 .remove(&server_id_to_remove);
7871 self.last_workspace_edits_by_language_server
7872 .remove(&server_id_to_remove);
7873 self.language_servers.remove(&server_id_to_remove);
7874 cx.emit(Event::LanguageServerRemoved(server_id_to_remove));
7875 }
7876
7877 let mut prettier_instances_to_clean = FuturesUnordered::new();
7878 if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
7879 for path in prettier_paths.iter().flatten() {
7880 if let Some(prettier_instance) = self.prettier_instances.remove(path) {
7881 prettier_instances_to_clean.push(async move {
7882 prettier_instance
7883 .server()
7884 .await
7885 .map(|server| server.server_id())
7886 });
7887 }
7888 }
7889 }
7890 cx.spawn(|project, mut cx| async move {
7891 while let Some(prettier_server_id) = prettier_instances_to_clean.next().await {
7892 if let Some(prettier_server_id) = prettier_server_id {
7893 project
7894 .update(&mut cx, |project, cx| {
7895 project
7896 .supplementary_language_servers
7897 .remove(&prettier_server_id);
7898 cx.emit(Event::LanguageServerRemoved(prettier_server_id));
7899 })
7900 .ok();
7901 }
7902 }
7903 })
7904 .detach();
7905
7906 self.task_inventory().update(cx, |inventory, _| {
7907 inventory.remove_worktree_sources(id_to_remove);
7908 });
7909
7910 self.worktree_store.update(cx, |worktree_store, cx| {
7911 worktree_store.remove_worktree(id_to_remove, cx);
7912 });
7913
7914 self.metadata_changed(cx);
7915 }
7916
7917 fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
7918 cx.observe(worktree, |_, _, cx| cx.notify()).detach();
7919 cx.subscribe(worktree, |this, worktree, event, cx| {
7920 let is_local = worktree.read(cx).is_local();
7921 match event {
7922 worktree::Event::UpdatedEntries(changes) => {
7923 if is_local {
7924 this.update_local_worktree_language_servers(&worktree, changes, cx);
7925 this.update_local_worktree_settings(&worktree, changes, cx);
7926 this.update_prettier_settings(&worktree, changes, cx);
7927 }
7928
7929 cx.emit(Event::WorktreeUpdatedEntries(
7930 worktree.read(cx).id(),
7931 changes.clone(),
7932 ));
7933
7934 let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
7935 this.client()
7936 .telemetry()
7937 .report_discovered_project_events(worktree_id, changes);
7938 }
7939 worktree::Event::UpdatedGitRepositories(_) => {
7940 cx.emit(Event::WorktreeUpdatedGitRepositories);
7941 }
7942 worktree::Event::DeletedEntry(id) => cx.emit(Event::DeletedEntry(*id)),
7943 }
7944 })
7945 .detach();
7946
7947 self.worktree_store.update(cx, |worktree_store, cx| {
7948 worktree_store.add(worktree, cx);
7949 });
7950 self.metadata_changed(cx);
7951 }
7952
7953 fn update_local_worktree_language_servers(
7954 &mut self,
7955 worktree_handle: &Model<Worktree>,
7956 changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
7957 cx: &mut ModelContext<Self>,
7958 ) {
7959 if changes.is_empty() {
7960 return;
7961 }
7962
7963 let worktree_id = worktree_handle.read(cx).id();
7964 let mut language_server_ids = self
7965 .language_server_ids
7966 .iter()
7967 .filter_map(|((server_worktree_id, _), server_id)| {
7968 (*server_worktree_id == worktree_id).then_some(*server_id)
7969 })
7970 .collect::<Vec<_>>();
7971 language_server_ids.sort();
7972 language_server_ids.dedup();
7973
7974 let abs_path = worktree_handle.read(cx).abs_path();
7975 for server_id in &language_server_ids {
7976 if let Some(LanguageServerState::Running { server, .. }) =
7977 self.language_servers.get(server_id)
7978 {
7979 if let Some(watched_paths) = self
7980 .language_server_watched_paths
7981 .get(&server_id)
7982 .and_then(|paths| paths.get(&worktree_id))
7983 {
7984 let params = lsp::DidChangeWatchedFilesParams {
7985 changes: changes
7986 .iter()
7987 .filter_map(|(path, _, change)| {
7988 if !watched_paths.is_match(&path) {
7989 return None;
7990 }
7991 let typ = match change {
7992 PathChange::Loaded => return None,
7993 PathChange::Added => lsp::FileChangeType::CREATED,
7994 PathChange::Removed => lsp::FileChangeType::DELETED,
7995 PathChange::Updated => lsp::FileChangeType::CHANGED,
7996 PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
7997 };
7998 Some(lsp::FileEvent {
7999 uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
8000 typ,
8001 })
8002 })
8003 .collect(),
8004 };
8005 if !params.changes.is_empty() {
8006 server
8007 .notify::<lsp::notification::DidChangeWatchedFiles>(params)
8008 .log_err();
8009 }
8010 }
8011 }
8012 }
8013 }
8014
8015 fn update_local_worktree_settings(
8016 &mut self,
8017 worktree: &Model<Worktree>,
8018 changes: &UpdatedEntriesSet,
8019 cx: &mut ModelContext<Self>,
8020 ) {
8021 if worktree.read(cx).is_remote() {
8022 return;
8023 }
8024 let project_id = self.remote_id();
8025 let worktree_id = worktree.entity_id();
8026 let remote_worktree_id = worktree.read(cx).id();
8027
8028 let mut settings_contents = Vec::new();
8029 for (path, _, change) in changes.iter() {
8030 let removed = change == &PathChange::Removed;
8031 let abs_path = match worktree.read(cx).absolutize(path) {
8032 Ok(abs_path) => abs_path,
8033 Err(e) => {
8034 log::warn!("Cannot absolutize {path:?} received as {change:?} FS change: {e}");
8035 continue;
8036 }
8037 };
8038
8039 if path.ends_with(local_settings_file_relative_path()) {
8040 let settings_dir = Arc::from(
8041 path.ancestors()
8042 .nth(local_settings_file_relative_path().components().count())
8043 .unwrap(),
8044 );
8045 let fs = self.fs.clone();
8046 settings_contents.push(async move {
8047 (
8048 settings_dir,
8049 if removed {
8050 None
8051 } else {
8052 Some(async move { fs.load(&abs_path).await }.await)
8053 },
8054 )
8055 });
8056 } else if path.ends_with(local_tasks_file_relative_path()) {
8057 self.task_inventory().update(cx, |task_inventory, cx| {
8058 if removed {
8059 task_inventory.remove_local_static_source(&abs_path);
8060 } else {
8061 let fs = self.fs.clone();
8062 let task_abs_path = abs_path.clone();
8063 let tasks_file_rx =
8064 watch_config_file(&cx.background_executor(), fs, task_abs_path);
8065 task_inventory.add_source(
8066 TaskSourceKind::Worktree {
8067 id: remote_worktree_id,
8068 abs_path,
8069 id_base: "local_tasks_for_worktree".into(),
8070 },
8071 |tx, cx| StaticSource::new(TrackedFile::new(tasks_file_rx, tx, cx)),
8072 cx,
8073 );
8074 }
8075 })
8076 } else if path.ends_with(local_vscode_tasks_file_relative_path()) {
8077 self.task_inventory().update(cx, |task_inventory, cx| {
8078 if removed {
8079 task_inventory.remove_local_static_source(&abs_path);
8080 } else {
8081 let fs = self.fs.clone();
8082 let task_abs_path = abs_path.clone();
8083 let tasks_file_rx =
8084 watch_config_file(&cx.background_executor(), fs, task_abs_path);
8085 task_inventory.add_source(
8086 TaskSourceKind::Worktree {
8087 id: remote_worktree_id,
8088 abs_path,
8089 id_base: "local_vscode_tasks_for_worktree".into(),
8090 },
8091 |tx, cx| {
8092 StaticSource::new(TrackedFile::new_convertible::<
8093 task::VsCodeTaskFile,
8094 >(
8095 tasks_file_rx, tx, cx
8096 ))
8097 },
8098 cx,
8099 );
8100 }
8101 })
8102 }
8103 }
8104
8105 if settings_contents.is_empty() {
8106 return;
8107 }
8108
8109 let client = self.client.clone();
8110 cx.spawn(move |_, cx| async move {
8111 let settings_contents: Vec<(Arc<Path>, _)> =
8112 futures::future::join_all(settings_contents).await;
8113 cx.update(|cx| {
8114 cx.update_global::<SettingsStore, _>(|store, cx| {
8115 for (directory, file_content) in settings_contents {
8116 let file_content = file_content.and_then(|content| content.log_err());
8117 store
8118 .set_local_settings(
8119 worktree_id.as_u64() as usize,
8120 directory.clone(),
8121 file_content.as_deref(),
8122 cx,
8123 )
8124 .log_err();
8125 if let Some(remote_id) = project_id {
8126 client
8127 .send(proto::UpdateWorktreeSettings {
8128 project_id: remote_id,
8129 worktree_id: remote_worktree_id.to_proto(),
8130 path: directory.to_string_lossy().into_owned(),
8131 content: file_content,
8132 })
8133 .log_err();
8134 }
8135 }
8136 });
8137 })
8138 .ok();
8139 })
8140 .detach();
8141 }
8142
8143 pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
8144 let new_active_entry = entry.and_then(|project_path| {
8145 let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
8146 let entry = worktree.read(cx).entry_for_path(project_path.path)?;
8147 Some(entry.id)
8148 });
8149 if new_active_entry != self.active_entry {
8150 self.active_entry = new_active_entry;
8151 cx.emit(Event::ActiveEntryChanged(new_active_entry));
8152 }
8153 }
8154
8155 pub fn language_servers_running_disk_based_diagnostics(
8156 &self,
8157 ) -> impl Iterator<Item = LanguageServerId> + '_ {
8158 self.language_server_statuses
8159 .iter()
8160 .filter_map(|(id, status)| {
8161 if status.has_pending_diagnostic_updates {
8162 Some(*id)
8163 } else {
8164 None
8165 }
8166 })
8167 }
8168
8169 pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
8170 let mut summary = DiagnosticSummary::default();
8171 for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
8172 summary.error_count += path_summary.error_count;
8173 summary.warning_count += path_summary.warning_count;
8174 }
8175 summary
8176 }
8177
8178 pub fn diagnostic_summaries<'a>(
8179 &'a self,
8180 include_ignored: bool,
8181 cx: &'a AppContext,
8182 ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
8183 self.visible_worktrees(cx)
8184 .filter_map(|worktree| {
8185 let worktree = worktree.read(cx);
8186 Some((worktree, self.diagnostic_summaries.get(&worktree.id())?))
8187 })
8188 .flat_map(move |(worktree, summaries)| {
8189 let worktree_id = worktree.id();
8190 summaries
8191 .iter()
8192 .filter(move |(path, _)| {
8193 include_ignored
8194 || worktree
8195 .entry_for_path(path.as_ref())
8196 .map_or(false, |entry| !entry.is_ignored)
8197 })
8198 .flat_map(move |(path, summaries)| {
8199 summaries.iter().map(move |(server_id, summary)| {
8200 (
8201 ProjectPath {
8202 worktree_id,
8203 path: path.clone(),
8204 },
8205 *server_id,
8206 *summary,
8207 )
8208 })
8209 })
8210 })
8211 }
8212
8213 pub fn disk_based_diagnostics_started(
8214 &mut self,
8215 language_server_id: LanguageServerId,
8216 cx: &mut ModelContext<Self>,
8217 ) {
8218 if let Some(language_server_status) =
8219 self.language_server_statuses.get_mut(&language_server_id)
8220 {
8221 language_server_status.has_pending_diagnostic_updates = true;
8222 }
8223
8224 cx.emit(Event::DiskBasedDiagnosticsStarted { language_server_id });
8225 if self.is_local_or_ssh() {
8226 self.enqueue_buffer_ordered_message(BufferOrderedMessage::LanguageServerUpdate {
8227 language_server_id,
8228 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
8229 Default::default(),
8230 ),
8231 })
8232 .ok();
8233 }
8234 }
8235
8236 pub fn disk_based_diagnostics_finished(
8237 &mut self,
8238 language_server_id: LanguageServerId,
8239 cx: &mut ModelContext<Self>,
8240 ) {
8241 if let Some(language_server_status) =
8242 self.language_server_statuses.get_mut(&language_server_id)
8243 {
8244 language_server_status.has_pending_diagnostic_updates = false;
8245 }
8246
8247 cx.emit(Event::DiskBasedDiagnosticsFinished { language_server_id });
8248
8249 if self.is_local_or_ssh() {
8250 self.enqueue_buffer_ordered_message(BufferOrderedMessage::LanguageServerUpdate {
8251 language_server_id,
8252 message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
8253 Default::default(),
8254 ),
8255 })
8256 .ok();
8257 }
8258 }
8259
8260 pub fn active_entry(&self) -> Option<ProjectEntryId> {
8261 self.active_entry
8262 }
8263
8264 pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
8265 self.worktree_for_id(path.worktree_id, cx)?
8266 .read(cx)
8267 .entry_for_path(&path.path)
8268 .cloned()
8269 }
8270
8271 pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
8272 let worktree = self.worktree_for_entry(entry_id, cx)?;
8273 let worktree = worktree.read(cx);
8274 let worktree_id = worktree.id();
8275 let path = worktree.entry_for_id(entry_id)?.path.clone();
8276 Some(ProjectPath { worktree_id, path })
8277 }
8278
8279 pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
8280 let workspace_root = self
8281 .worktree_for_id(project_path.worktree_id, cx)?
8282 .read(cx)
8283 .abs_path();
8284 let project_path = project_path.path.as_ref();
8285
8286 Some(if project_path == Path::new("") {
8287 workspace_root.to_path_buf()
8288 } else {
8289 workspace_root.join(project_path)
8290 })
8291 }
8292
8293 /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
8294 /// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
8295 /// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
8296 /// the first visible worktree that has an entry for that relative path.
8297 ///
8298 /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
8299 /// root name from paths.
8300 ///
8301 /// # Arguments
8302 ///
8303 /// * `path` - A full path that starts with a worktree root name, or alternatively a
8304 /// relative path within a visible worktree.
8305 /// * `cx` - A reference to the `AppContext`.
8306 ///
8307 /// # Returns
8308 ///
8309 /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
8310 pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option<ProjectPath> {
8311 let worktree_store = self.worktree_store.read(cx);
8312
8313 for worktree in worktree_store.visible_worktrees(cx) {
8314 let worktree_root_name = worktree.read(cx).root_name();
8315 if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
8316 return Some(ProjectPath {
8317 worktree_id: worktree.read(cx).id(),
8318 path: relative_path.into(),
8319 });
8320 }
8321 }
8322
8323 for worktree in worktree_store.visible_worktrees(cx) {
8324 let worktree = worktree.read(cx);
8325 if let Some(entry) = worktree.entry_for_path(path) {
8326 return Some(ProjectPath {
8327 worktree_id: worktree.id(),
8328 path: entry.path.clone(),
8329 });
8330 }
8331 }
8332
8333 None
8334 }
8335
8336 pub fn get_workspace_root(
8337 &self,
8338 project_path: &ProjectPath,
8339 cx: &AppContext,
8340 ) -> Option<PathBuf> {
8341 Some(
8342 self.worktree_for_id(project_path.worktree_id, cx)?
8343 .read(cx)
8344 .abs_path()
8345 .to_path_buf(),
8346 )
8347 }
8348
8349 pub fn get_repo(
8350 &self,
8351 project_path: &ProjectPath,
8352 cx: &AppContext,
8353 ) -> Option<Arc<dyn GitRepository>> {
8354 self.worktree_for_id(project_path.worktree_id, cx)?
8355 .read(cx)
8356 .as_local()?
8357 .local_git_repo(&project_path.path)
8358 }
8359
8360 pub fn get_first_worktree_root_repo(&self, cx: &AppContext) -> Option<Arc<dyn GitRepository>> {
8361 let worktree = self.visible_worktrees(cx).next()?.read(cx).as_local()?;
8362 let root_entry = worktree.root_git_entry()?;
8363 worktree.get_local_repo(&root_entry)?.repo().clone().into()
8364 }
8365
8366 pub fn blame_buffer(
8367 &self,
8368 buffer: &Model<Buffer>,
8369 version: Option<clock::Global>,
8370 cx: &AppContext,
8371 ) -> Task<Result<Blame>> {
8372 self.buffer_store.read(cx).blame_buffer(buffer, version, cx)
8373 }
8374
8375 // RPC message handlers
8376
8377 async fn handle_multi_lsp_query(
8378 project: Model<Self>,
8379 envelope: TypedEnvelope<proto::MultiLspQuery>,
8380 mut cx: AsyncAppContext,
8381 ) -> Result<proto::MultiLspQueryResponse> {
8382 let sender_id = envelope.original_sender_id()?;
8383 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
8384 let version = deserialize_version(&envelope.payload.version);
8385 let buffer = project.update(&mut cx, |project, cx| {
8386 project.buffer_store.read(cx).get_existing(buffer_id)
8387 })??;
8388 buffer
8389 .update(&mut cx, |buffer, _| {
8390 buffer.wait_for_version(version.clone())
8391 })?
8392 .await?;
8393 let buffer_version = buffer.update(&mut cx, |buffer, _| buffer.version())?;
8394 match envelope
8395 .payload
8396 .strategy
8397 .context("invalid request without the strategy")?
8398 {
8399 proto::multi_lsp_query::Strategy::All(_) => {
8400 // currently, there's only one multiple language servers query strategy,
8401 // so just ensure it's specified correctly
8402 }
8403 }
8404 match envelope.payload.request {
8405 Some(proto::multi_lsp_query::Request::GetHover(get_hover)) => {
8406 let get_hover =
8407 GetHover::from_proto(get_hover, project.clone(), buffer.clone(), cx.clone())
8408 .await?;
8409 let all_hovers = project
8410 .update(&mut cx, |project, cx| {
8411 project.request_multiple_lsp_locally(
8412 &buffer,
8413 Some(get_hover.position),
8414 get_hover,
8415 cx,
8416 )
8417 })?
8418 .await
8419 .into_iter()
8420 .filter_map(|hover| remove_empty_hover_blocks(hover?));
8421 project.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
8422 responses: all_hovers
8423 .map(|hover| proto::LspResponse {
8424 response: Some(proto::lsp_response::Response::GetHoverResponse(
8425 GetHover::response_to_proto(
8426 Some(hover),
8427 project,
8428 sender_id,
8429 &buffer_version,
8430 cx,
8431 ),
8432 )),
8433 })
8434 .collect(),
8435 })
8436 }
8437 Some(proto::multi_lsp_query::Request::GetCodeActions(get_code_actions)) => {
8438 let get_code_actions = GetCodeActions::from_proto(
8439 get_code_actions,
8440 project.clone(),
8441 buffer.clone(),
8442 cx.clone(),
8443 )
8444 .await?;
8445
8446 let all_actions = project
8447 .update(&mut cx, |project, cx| {
8448 project.request_multiple_lsp_locally(
8449 &buffer,
8450 Some(get_code_actions.range.start),
8451 get_code_actions,
8452 cx,
8453 )
8454 })?
8455 .await
8456 .into_iter();
8457
8458 project.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
8459 responses: all_actions
8460 .map(|code_actions| proto::LspResponse {
8461 response: Some(proto::lsp_response::Response::GetCodeActionsResponse(
8462 GetCodeActions::response_to_proto(
8463 code_actions,
8464 project,
8465 sender_id,
8466 &buffer_version,
8467 cx,
8468 ),
8469 )),
8470 })
8471 .collect(),
8472 })
8473 }
8474 Some(proto::multi_lsp_query::Request::GetSignatureHelp(get_signature_help)) => {
8475 let get_signature_help = GetSignatureHelp::from_proto(
8476 get_signature_help,
8477 project.clone(),
8478 buffer.clone(),
8479 cx.clone(),
8480 )
8481 .await?;
8482
8483 let all_signatures = project
8484 .update(&mut cx, |project, cx| {
8485 project.request_multiple_lsp_locally(
8486 &buffer,
8487 Some(get_signature_help.position),
8488 get_signature_help,
8489 cx,
8490 )
8491 })?
8492 .await
8493 .into_iter();
8494
8495 project.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
8496 responses: all_signatures
8497 .map(|signature_help| proto::LspResponse {
8498 response: Some(
8499 proto::lsp_response::Response::GetSignatureHelpResponse(
8500 GetSignatureHelp::response_to_proto(
8501 signature_help,
8502 project,
8503 sender_id,
8504 &buffer_version,
8505 cx,
8506 ),
8507 ),
8508 ),
8509 })
8510 .collect(),
8511 })
8512 }
8513 None => anyhow::bail!("empty multi lsp query request"),
8514 }
8515 }
8516
8517 async fn handle_unshare_project(
8518 this: Model<Self>,
8519 _: TypedEnvelope<proto::UnshareProject>,
8520 mut cx: AsyncAppContext,
8521 ) -> Result<()> {
8522 this.update(&mut cx, |this, cx| {
8523 if this.is_local_or_ssh() {
8524 this.unshare(cx)?;
8525 } else {
8526 this.disconnected_from_host(cx);
8527 }
8528 Ok(())
8529 })?
8530 }
8531
8532 async fn handle_add_collaborator(
8533 this: Model<Self>,
8534 mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
8535 mut cx: AsyncAppContext,
8536 ) -> Result<()> {
8537 let collaborator = envelope
8538 .payload
8539 .collaborator
8540 .take()
8541 .ok_or_else(|| anyhow!("empty collaborator"))?;
8542
8543 let collaborator = Collaborator::from_proto(collaborator)?;
8544 this.update(&mut cx, |this, cx| {
8545 this.shared_buffers.remove(&collaborator.peer_id);
8546 cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
8547 this.collaborators
8548 .insert(collaborator.peer_id, collaborator);
8549 cx.notify();
8550 })?;
8551
8552 Ok(())
8553 }
8554
8555 async fn handle_update_project_collaborator(
8556 this: Model<Self>,
8557 envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
8558 mut cx: AsyncAppContext,
8559 ) -> Result<()> {
8560 let old_peer_id = envelope
8561 .payload
8562 .old_peer_id
8563 .ok_or_else(|| anyhow!("missing old peer id"))?;
8564 let new_peer_id = envelope
8565 .payload
8566 .new_peer_id
8567 .ok_or_else(|| anyhow!("missing new peer id"))?;
8568 this.update(&mut cx, |this, cx| {
8569 let collaborator = this
8570 .collaborators
8571 .remove(&old_peer_id)
8572 .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
8573 let is_host = collaborator.replica_id == 0;
8574 this.collaborators.insert(new_peer_id, collaborator);
8575
8576 let buffers = this.shared_buffers.remove(&old_peer_id);
8577 log::info!(
8578 "peer {} became {}. moving buffers {:?}",
8579 old_peer_id,
8580 new_peer_id,
8581 &buffers
8582 );
8583 if let Some(buffers) = buffers {
8584 this.shared_buffers.insert(new_peer_id, buffers);
8585 }
8586
8587 if is_host {
8588 this.buffer_store
8589 .update(cx, |buffer_store, _| buffer_store.discard_incomplete());
8590 this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
8591 .unwrap();
8592 cx.emit(Event::HostReshared);
8593 }
8594
8595 cx.emit(Event::CollaboratorUpdated {
8596 old_peer_id,
8597 new_peer_id,
8598 });
8599 cx.notify();
8600 Ok(())
8601 })?
8602 }
8603
8604 async fn handle_remove_collaborator(
8605 this: Model<Self>,
8606 envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
8607 mut cx: AsyncAppContext,
8608 ) -> Result<()> {
8609 this.update(&mut cx, |this, cx| {
8610 let peer_id = envelope
8611 .payload
8612 .peer_id
8613 .ok_or_else(|| anyhow!("invalid peer id"))?;
8614 let replica_id = this
8615 .collaborators
8616 .remove(&peer_id)
8617 .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
8618 .replica_id;
8619 this.buffer_store.update(cx, |buffer_store, cx| {
8620 for buffer in buffer_store.buffers() {
8621 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
8622 }
8623 });
8624 this.shared_buffers.remove(&peer_id);
8625
8626 cx.emit(Event::CollaboratorLeft(peer_id));
8627 cx.notify();
8628 Ok(())
8629 })?
8630 }
8631
8632 async fn handle_update_project(
8633 this: Model<Self>,
8634 envelope: TypedEnvelope<proto::UpdateProject>,
8635 mut cx: AsyncAppContext,
8636 ) -> Result<()> {
8637 this.update(&mut cx, |this, cx| {
8638 // Don't handle messages that were sent before the response to us joining the project
8639 if envelope.message_id > this.join_project_response_message_id {
8640 this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
8641 }
8642 Ok(())
8643 })?
8644 }
8645
8646 async fn handle_update_worktree(
8647 this: Model<Self>,
8648 envelope: TypedEnvelope<proto::UpdateWorktree>,
8649 mut cx: AsyncAppContext,
8650 ) -> Result<()> {
8651 this.update(&mut cx, |this, cx| {
8652 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
8653 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
8654 worktree.update(cx, |worktree, _| {
8655 let worktree = worktree.as_remote_mut().unwrap();
8656 worktree.update_from_remote(envelope.payload);
8657 });
8658 }
8659 Ok(())
8660 })?
8661 }
8662
8663 async fn handle_update_worktree_settings(
8664 this: Model<Self>,
8665 envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
8666 mut cx: AsyncAppContext,
8667 ) -> Result<()> {
8668 this.update(&mut cx, |this, cx| {
8669 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
8670 if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
8671 cx.update_global::<SettingsStore, _>(|store, cx| {
8672 store
8673 .set_local_settings(
8674 worktree.entity_id().as_u64() as usize,
8675 PathBuf::from(&envelope.payload.path).into(),
8676 envelope.payload.content.as_deref(),
8677 cx,
8678 )
8679 .log_err();
8680 });
8681 }
8682 Ok(())
8683 })?
8684 }
8685
8686 async fn handle_update_diagnostic_summary(
8687 this: Model<Self>,
8688 envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
8689 mut cx: AsyncAppContext,
8690 ) -> Result<()> {
8691 this.update(&mut cx, |this, cx| {
8692 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
8693 if let Some(message) = envelope.payload.summary {
8694 let project_path = ProjectPath {
8695 worktree_id,
8696 path: Path::new(&message.path).into(),
8697 };
8698 let path = project_path.path.clone();
8699 let server_id = LanguageServerId(message.language_server_id as usize);
8700 let summary = DiagnosticSummary {
8701 error_count: message.error_count as usize,
8702 warning_count: message.warning_count as usize,
8703 };
8704
8705 if summary.is_empty() {
8706 if let Some(worktree_summaries) =
8707 this.diagnostic_summaries.get_mut(&worktree_id)
8708 {
8709 if let Some(summaries) = worktree_summaries.get_mut(&path) {
8710 summaries.remove(&server_id);
8711 if summaries.is_empty() {
8712 worktree_summaries.remove(&path);
8713 }
8714 }
8715 }
8716 } else {
8717 this.diagnostic_summaries
8718 .entry(worktree_id)
8719 .or_default()
8720 .entry(path)
8721 .or_default()
8722 .insert(server_id, summary);
8723 }
8724 cx.emit(Event::DiagnosticsUpdated {
8725 language_server_id: LanguageServerId(message.language_server_id as usize),
8726 path: project_path,
8727 });
8728 }
8729 Ok(())
8730 })?
8731 }
8732
8733 async fn handle_start_language_server(
8734 this: Model<Self>,
8735 envelope: TypedEnvelope<proto::StartLanguageServer>,
8736 mut cx: AsyncAppContext,
8737 ) -> Result<()> {
8738 let server = envelope
8739 .payload
8740 .server
8741 .ok_or_else(|| anyhow!("invalid server"))?;
8742 this.update(&mut cx, |this, cx| {
8743 this.language_server_statuses.insert(
8744 LanguageServerId(server.id as usize),
8745 LanguageServerStatus {
8746 name: server.name,
8747 pending_work: Default::default(),
8748 has_pending_diagnostic_updates: false,
8749 progress_tokens: Default::default(),
8750 },
8751 );
8752 cx.notify();
8753 })?;
8754 Ok(())
8755 }
8756
8757 async fn handle_update_language_server(
8758 this: Model<Self>,
8759 envelope: TypedEnvelope<proto::UpdateLanguageServer>,
8760 mut cx: AsyncAppContext,
8761 ) -> Result<()> {
8762 this.update(&mut cx, |this, cx| {
8763 let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
8764
8765 match envelope
8766 .payload
8767 .variant
8768 .ok_or_else(|| anyhow!("invalid variant"))?
8769 {
8770 proto::update_language_server::Variant::WorkStart(payload) => {
8771 this.on_lsp_work_start(
8772 language_server_id,
8773 payload.token,
8774 LanguageServerProgress {
8775 title: payload.title,
8776 is_disk_based_diagnostics_progress: false,
8777 is_cancellable: false,
8778 message: payload.message,
8779 percentage: payload.percentage.map(|p| p as usize),
8780 last_update_at: cx.background_executor().now(),
8781 },
8782 cx,
8783 );
8784 }
8785
8786 proto::update_language_server::Variant::WorkProgress(payload) => {
8787 this.on_lsp_work_progress(
8788 language_server_id,
8789 payload.token,
8790 LanguageServerProgress {
8791 title: None,
8792 is_disk_based_diagnostics_progress: false,
8793 is_cancellable: false,
8794 message: payload.message,
8795 percentage: payload.percentage.map(|p| p as usize),
8796 last_update_at: cx.background_executor().now(),
8797 },
8798 cx,
8799 );
8800 }
8801
8802 proto::update_language_server::Variant::WorkEnd(payload) => {
8803 this.on_lsp_work_end(language_server_id, payload.token, cx);
8804 }
8805
8806 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
8807 this.disk_based_diagnostics_started(language_server_id, cx);
8808 }
8809
8810 proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
8811 this.disk_based_diagnostics_finished(language_server_id, cx)
8812 }
8813 }
8814
8815 Ok(())
8816 })?
8817 }
8818
8819 async fn handle_update_buffer(
8820 this: Model<Self>,
8821 envelope: TypedEnvelope<proto::UpdateBuffer>,
8822 cx: AsyncAppContext,
8823 ) -> Result<proto::Ack> {
8824 let buffer_store = this.read_with(&cx, |this, cx| {
8825 if let Some(ssh) = &this.ssh_session {
8826 let mut payload = envelope.payload.clone();
8827 payload.project_id = 0;
8828 cx.background_executor()
8829 .spawn(ssh.request(payload))
8830 .detach_and_log_err(cx);
8831 }
8832 this.buffer_store.clone()
8833 })?;
8834 BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
8835 }
8836
8837 fn retain_remotely_created_buffers(&mut self) -> RemotelyCreatedBufferGuard {
8838 self.remotely_created_buffers.lock().retain_count += 1;
8839 RemotelyCreatedBufferGuard {
8840 remote_buffers: Arc::downgrade(&self.remotely_created_buffers),
8841 }
8842 }
8843
8844 async fn handle_create_buffer_for_peer(
8845 this: Model<Self>,
8846 envelope: TypedEnvelope<proto::CreateBufferForPeer>,
8847 mut cx: AsyncAppContext,
8848 ) -> Result<()> {
8849 this.update(&mut cx, |this, cx| {
8850 this.buffer_store.update(cx, |buffer_store, cx| {
8851 buffer_store.handle_create_buffer_for_peer(
8852 envelope,
8853 this.replica_id(),
8854 this.capability(),
8855 cx,
8856 )
8857 })
8858 })?
8859 }
8860
8861 async fn handle_reload_buffers(
8862 this: Model<Self>,
8863 envelope: TypedEnvelope<proto::ReloadBuffers>,
8864 mut cx: AsyncAppContext,
8865 ) -> Result<proto::ReloadBuffersResponse> {
8866 let sender_id = envelope.original_sender_id()?;
8867 let reload = this.update(&mut cx, |this, cx| {
8868 let mut buffers = HashSet::default();
8869 for buffer_id in &envelope.payload.buffer_ids {
8870 let buffer_id = BufferId::new(*buffer_id)?;
8871 buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
8872 }
8873 Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
8874 })??;
8875
8876 let project_transaction = reload.await?;
8877 let project_transaction = this.update(&mut cx, |this, cx| {
8878 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
8879 })?;
8880 Ok(proto::ReloadBuffersResponse {
8881 transaction: Some(project_transaction),
8882 })
8883 }
8884
8885 async fn handle_synchronize_buffers(
8886 this: Model<Self>,
8887 envelope: TypedEnvelope<proto::SynchronizeBuffers>,
8888 mut cx: AsyncAppContext,
8889 ) -> Result<proto::SynchronizeBuffersResponse> {
8890 let project_id = envelope.payload.project_id;
8891 let mut response = proto::SynchronizeBuffersResponse {
8892 buffers: Default::default(),
8893 };
8894
8895 this.update(&mut cx, |this, cx| {
8896 let Some(guest_id) = envelope.original_sender_id else {
8897 error!("missing original_sender_id on SynchronizeBuffers request");
8898 bail!("missing original_sender_id on SynchronizeBuffers request");
8899 };
8900
8901 this.shared_buffers.entry(guest_id).or_default().clear();
8902 for buffer in envelope.payload.buffers {
8903 let buffer_id = BufferId::new(buffer.id)?;
8904 let remote_version = language::proto::deserialize_version(&buffer.version);
8905 if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
8906 this.shared_buffers
8907 .entry(guest_id)
8908 .or_default()
8909 .insert(buffer_id);
8910
8911 let buffer = buffer.read(cx);
8912 response.buffers.push(proto::BufferVersion {
8913 id: buffer_id.into(),
8914 version: language::proto::serialize_version(&buffer.version),
8915 });
8916
8917 let operations = buffer.serialize_ops(Some(remote_version), cx);
8918 let client = this.client.clone();
8919 if let Some(file) = buffer.file() {
8920 client
8921 .send(proto::UpdateBufferFile {
8922 project_id,
8923 buffer_id: buffer_id.into(),
8924 file: Some(file.to_proto(cx)),
8925 })
8926 .log_err();
8927 }
8928
8929 client
8930 .send(proto::UpdateDiffBase {
8931 project_id,
8932 buffer_id: buffer_id.into(),
8933 diff_base: buffer.diff_base().map(ToString::to_string),
8934 })
8935 .log_err();
8936
8937 client
8938 .send(proto::BufferReloaded {
8939 project_id,
8940 buffer_id: buffer_id.into(),
8941 version: language::proto::serialize_version(buffer.saved_version()),
8942 mtime: buffer.saved_mtime().map(|time| time.into()),
8943 line_ending: language::proto::serialize_line_ending(
8944 buffer.line_ending(),
8945 ) as i32,
8946 })
8947 .log_err();
8948
8949 cx.background_executor()
8950 .spawn(
8951 async move {
8952 let operations = operations.await;
8953 for chunk in split_operations(operations) {
8954 client
8955 .request(proto::UpdateBuffer {
8956 project_id,
8957 buffer_id: buffer_id.into(),
8958 operations: chunk,
8959 })
8960 .await?;
8961 }
8962 anyhow::Ok(())
8963 }
8964 .log_err(),
8965 )
8966 .detach();
8967 }
8968 }
8969 Ok(())
8970 })??;
8971
8972 Ok(response)
8973 }
8974
8975 async fn handle_format_buffers(
8976 this: Model<Self>,
8977 envelope: TypedEnvelope<proto::FormatBuffers>,
8978 mut cx: AsyncAppContext,
8979 ) -> Result<proto::FormatBuffersResponse> {
8980 let sender_id = envelope.original_sender_id()?;
8981 let format = this.update(&mut cx, |this, cx| {
8982 let mut buffers = HashSet::default();
8983 for buffer_id in &envelope.payload.buffer_ids {
8984 let buffer_id = BufferId::new(*buffer_id)?;
8985 buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
8986 }
8987 let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
8988 Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
8989 })??;
8990
8991 let project_transaction = format.await?;
8992 let project_transaction = this.update(&mut cx, |this, cx| {
8993 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
8994 })?;
8995 Ok(proto::FormatBuffersResponse {
8996 transaction: Some(project_transaction),
8997 })
8998 }
8999
9000 async fn handle_apply_additional_edits_for_completion(
9001 this: Model<Self>,
9002 envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
9003 mut cx: AsyncAppContext,
9004 ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
9005 let (buffer, completion) = this.update(&mut cx, |this, cx| {
9006 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9007 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
9008 let completion = Self::deserialize_completion(
9009 envelope
9010 .payload
9011 .completion
9012 .ok_or_else(|| anyhow!("invalid completion"))?,
9013 )?;
9014 anyhow::Ok((buffer, completion))
9015 })??;
9016
9017 let apply_additional_edits = this.update(&mut cx, |this, cx| {
9018 this.apply_additional_edits_for_completion(
9019 buffer,
9020 Completion {
9021 old_range: completion.old_range,
9022 new_text: completion.new_text,
9023 lsp_completion: completion.lsp_completion,
9024 server_id: completion.server_id,
9025 documentation: None,
9026 label: CodeLabel {
9027 text: Default::default(),
9028 runs: Default::default(),
9029 filter_range: Default::default(),
9030 },
9031 confirm: None,
9032 },
9033 false,
9034 cx,
9035 )
9036 })?;
9037
9038 Ok(proto::ApplyCompletionAdditionalEditsResponse {
9039 transaction: apply_additional_edits
9040 .await?
9041 .as_ref()
9042 .map(language::proto::serialize_transaction),
9043 })
9044 }
9045
9046 async fn handle_resolve_completion_documentation(
9047 this: Model<Self>,
9048 envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
9049 mut cx: AsyncAppContext,
9050 ) -> Result<proto::ResolveCompletionDocumentationResponse> {
9051 let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
9052
9053 let completion = this
9054 .read_with(&mut cx, |this, _| {
9055 let id = LanguageServerId(envelope.payload.language_server_id as usize);
9056 let Some(server) = this.language_server_for_id(id) else {
9057 return Err(anyhow!("No language server {id}"));
9058 };
9059
9060 Ok(server.request::<lsp::request::ResolveCompletionItem>(lsp_completion))
9061 })??
9062 .await?;
9063
9064 let mut documentation_is_markdown = false;
9065 let documentation = match completion.documentation {
9066 Some(lsp::Documentation::String(text)) => text,
9067
9068 Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
9069 documentation_is_markdown = kind == lsp::MarkupKind::Markdown;
9070 value
9071 }
9072
9073 _ => String::new(),
9074 };
9075
9076 // If we have a new buffer_id, that means we're talking to a new client
9077 // and want to check for new text_edits in the completion too.
9078 let mut old_start = None;
9079 let mut old_end = None;
9080 let mut new_text = String::default();
9081 if let Ok(buffer_id) = BufferId::new(envelope.payload.buffer_id) {
9082 let buffer_snapshot = this.update(&mut cx, |this, cx| {
9083 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
9084 anyhow::Ok(buffer.read(cx).snapshot())
9085 })??;
9086
9087 if let Some(text_edit) = completion.text_edit.as_ref() {
9088 let edit = parse_completion_text_edit(text_edit, &buffer_snapshot);
9089
9090 if let Some((old_range, mut text_edit_new_text)) = edit {
9091 LineEnding::normalize(&mut text_edit_new_text);
9092
9093 new_text = text_edit_new_text;
9094 old_start = Some(serialize_anchor(&old_range.start));
9095 old_end = Some(serialize_anchor(&old_range.end));
9096 }
9097 }
9098 }
9099
9100 Ok(proto::ResolveCompletionDocumentationResponse {
9101 documentation,
9102 documentation_is_markdown,
9103 old_start,
9104 old_end,
9105 new_text,
9106 })
9107 }
9108
9109 async fn handle_apply_code_action(
9110 this: Model<Self>,
9111 envelope: TypedEnvelope<proto::ApplyCodeAction>,
9112 mut cx: AsyncAppContext,
9113 ) -> Result<proto::ApplyCodeActionResponse> {
9114 let sender_id = envelope.original_sender_id()?;
9115 let action = Self::deserialize_code_action(
9116 envelope
9117 .payload
9118 .action
9119 .ok_or_else(|| anyhow!("invalid action"))?,
9120 )?;
9121 let apply_code_action = this.update(&mut cx, |this, cx| {
9122 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9123 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
9124 anyhow::Ok(this.apply_code_action(buffer, action, false, cx))
9125 })??;
9126
9127 let project_transaction = apply_code_action.await?;
9128 let project_transaction = this.update(&mut cx, |this, cx| {
9129 this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
9130 })?;
9131 Ok(proto::ApplyCodeActionResponse {
9132 transaction: Some(project_transaction),
9133 })
9134 }
9135
9136 async fn handle_on_type_formatting(
9137 this: Model<Self>,
9138 envelope: TypedEnvelope<proto::OnTypeFormatting>,
9139 mut cx: AsyncAppContext,
9140 ) -> Result<proto::OnTypeFormattingResponse> {
9141 let on_type_formatting = this.update(&mut cx, |this, cx| {
9142 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9143 let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
9144 let position = envelope
9145 .payload
9146 .position
9147 .and_then(deserialize_anchor)
9148 .ok_or_else(|| anyhow!("invalid position"))?;
9149 Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
9150 buffer,
9151 position,
9152 envelope.payload.trigger.clone(),
9153 cx,
9154 ))
9155 })??;
9156
9157 let transaction = on_type_formatting
9158 .await?
9159 .as_ref()
9160 .map(language::proto::serialize_transaction);
9161 Ok(proto::OnTypeFormattingResponse { transaction })
9162 }
9163
9164 async fn handle_inlay_hints(
9165 this: Model<Self>,
9166 envelope: TypedEnvelope<proto::InlayHints>,
9167 mut cx: AsyncAppContext,
9168 ) -> Result<proto::InlayHintsResponse> {
9169 let sender_id = envelope.original_sender_id()?;
9170 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9171 let buffer = this.update(&mut cx, |this, cx| {
9172 this.buffer_store.read(cx).get_existing(buffer_id)
9173 })??;
9174 buffer
9175 .update(&mut cx, |buffer, _| {
9176 buffer.wait_for_version(deserialize_version(&envelope.payload.version))
9177 })?
9178 .await
9179 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
9180
9181 let start = envelope
9182 .payload
9183 .start
9184 .and_then(deserialize_anchor)
9185 .context("missing range start")?;
9186 let end = envelope
9187 .payload
9188 .end
9189 .and_then(deserialize_anchor)
9190 .context("missing range end")?;
9191 let buffer_hints = this
9192 .update(&mut cx, |project, cx| {
9193 project.inlay_hints(buffer.clone(), start..end, cx)
9194 })?
9195 .await
9196 .context("inlay hints fetch")?;
9197
9198 this.update(&mut cx, |project, cx| {
9199 InlayHints::response_to_proto(
9200 buffer_hints,
9201 project,
9202 sender_id,
9203 &buffer.read(cx).version(),
9204 cx,
9205 )
9206 })
9207 }
9208
9209 async fn handle_resolve_inlay_hint(
9210 this: Model<Self>,
9211 envelope: TypedEnvelope<proto::ResolveInlayHint>,
9212 mut cx: AsyncAppContext,
9213 ) -> Result<proto::ResolveInlayHintResponse> {
9214 let proto_hint = envelope
9215 .payload
9216 .hint
9217 .expect("incorrect protobuf resolve inlay hint message: missing the inlay hint");
9218 let hint = InlayHints::proto_to_project_hint(proto_hint)
9219 .context("resolved proto inlay hint conversion")?;
9220 let buffer = this.update(&mut cx, |this, cx| {
9221 let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9222 this.buffer_store.read(cx).get_existing(buffer_id)
9223 })??;
9224 let response_hint = this
9225 .update(&mut cx, |project, cx| {
9226 project.resolve_inlay_hint(
9227 hint,
9228 buffer,
9229 LanguageServerId(envelope.payload.language_server_id as usize),
9230 cx,
9231 )
9232 })?
9233 .await
9234 .context("inlay hints fetch")?;
9235 Ok(proto::ResolveInlayHintResponse {
9236 hint: Some(InlayHints::project_to_proto_hint(response_hint)),
9237 })
9238 }
9239
9240 async fn handle_task_context_for_location(
9241 project: Model<Self>,
9242 envelope: TypedEnvelope<proto::TaskContextForLocation>,
9243 mut cx: AsyncAppContext,
9244 ) -> Result<proto::TaskContext> {
9245 let location = envelope
9246 .payload
9247 .location
9248 .context("no location given for task context handling")?;
9249 let location = cx
9250 .update(|cx| deserialize_location(&project, location, cx))?
9251 .await?;
9252 let context_task = project.update(&mut cx, |project, cx| {
9253 let captured_variables = {
9254 let mut variables = TaskVariables::default();
9255 for range in location
9256 .buffer
9257 .read(cx)
9258 .snapshot()
9259 .runnable_ranges(location.range.clone())
9260 {
9261 for (capture_name, value) in range.extra_captures {
9262 variables.insert(VariableName::Custom(capture_name.into()), value);
9263 }
9264 }
9265 variables
9266 };
9267 project.task_context_for_location(captured_variables, location, cx)
9268 })?;
9269 let task_context = context_task.await.unwrap_or_default();
9270 Ok(proto::TaskContext {
9271 project_env: task_context.project_env.into_iter().collect(),
9272 cwd: task_context
9273 .cwd
9274 .map(|cwd| cwd.to_string_lossy().to_string()),
9275 task_variables: task_context
9276 .task_variables
9277 .into_iter()
9278 .map(|(variable_name, variable_value)| (variable_name.to_string(), variable_value))
9279 .collect(),
9280 })
9281 }
9282
9283 async fn handle_task_templates(
9284 project: Model<Self>,
9285 envelope: TypedEnvelope<proto::TaskTemplates>,
9286 mut cx: AsyncAppContext,
9287 ) -> Result<proto::TaskTemplatesResponse> {
9288 let worktree = envelope.payload.worktree_id.map(WorktreeId::from_proto);
9289 let location = match envelope.payload.location {
9290 Some(location) => Some(
9291 cx.update(|cx| deserialize_location(&project, location, cx))?
9292 .await
9293 .context("task templates request location deserializing")?,
9294 ),
9295 None => None,
9296 };
9297
9298 let templates = project
9299 .update(&mut cx, |project, cx| {
9300 project.task_templates(worktree, location, cx)
9301 })?
9302 .await
9303 .context("receiving task templates")?
9304 .into_iter()
9305 .map(|(kind, template)| {
9306 let kind = Some(match kind {
9307 TaskSourceKind::UserInput => proto::task_source_kind::Kind::UserInput(
9308 proto::task_source_kind::UserInput {},
9309 ),
9310 TaskSourceKind::Worktree {
9311 id,
9312 abs_path,
9313 id_base,
9314 } => {
9315 proto::task_source_kind::Kind::Worktree(proto::task_source_kind::Worktree {
9316 id: id.to_proto(),
9317 abs_path: abs_path.to_string_lossy().to_string(),
9318 id_base: id_base.to_string(),
9319 })
9320 }
9321 TaskSourceKind::AbsPath { id_base, abs_path } => {
9322 proto::task_source_kind::Kind::AbsPath(proto::task_source_kind::AbsPath {
9323 abs_path: abs_path.to_string_lossy().to_string(),
9324 id_base: id_base.to_string(),
9325 })
9326 }
9327 TaskSourceKind::Language { name } => {
9328 proto::task_source_kind::Kind::Language(proto::task_source_kind::Language {
9329 name: name.to_string(),
9330 })
9331 }
9332 });
9333 let kind = Some(proto::TaskSourceKind { kind });
9334 let template = Some(proto::TaskTemplate {
9335 label: template.label,
9336 command: template.command,
9337 args: template.args,
9338 env: template.env.into_iter().collect(),
9339 cwd: template.cwd,
9340 use_new_terminal: template.use_new_terminal,
9341 allow_concurrent_runs: template.allow_concurrent_runs,
9342 reveal: match template.reveal {
9343 RevealStrategy::Always => proto::RevealStrategy::RevealAlways as i32,
9344 RevealStrategy::Never => proto::RevealStrategy::RevealNever as i32,
9345 },
9346 hide: match template.hide {
9347 HideStrategy::Always => proto::HideStrategy::HideAlways as i32,
9348 HideStrategy::Never => proto::HideStrategy::HideNever as i32,
9349 HideStrategy::OnSuccess => proto::HideStrategy::HideOnSuccess as i32,
9350 },
9351 shell: Some(proto::Shell {
9352 shell_type: Some(match template.shell {
9353 Shell::System => proto::shell::ShellType::System(proto::System {}),
9354 Shell::Program(program) => proto::shell::ShellType::Program(program),
9355 Shell::WithArguments { program, args } => {
9356 proto::shell::ShellType::WithArguments(
9357 proto::shell::WithArguments { program, args },
9358 )
9359 }
9360 }),
9361 }),
9362 tags: template.tags,
9363 });
9364 proto::TemplatePair { kind, template }
9365 })
9366 .collect();
9367
9368 Ok(proto::TaskTemplatesResponse { templates })
9369 }
9370
9371 async fn try_resolve_code_action(
9372 lang_server: &LanguageServer,
9373 action: &mut CodeAction,
9374 ) -> anyhow::Result<()> {
9375 if GetCodeActions::can_resolve_actions(&lang_server.capabilities()) {
9376 if action.lsp_action.data.is_some()
9377 && (action.lsp_action.command.is_none() || action.lsp_action.edit.is_none())
9378 {
9379 action.lsp_action = lang_server
9380 .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action.clone())
9381 .await?;
9382 }
9383 }
9384
9385 anyhow::Ok(())
9386 }
9387
9388 async fn execute_code_actions_on_servers(
9389 project: &WeakModel<Project>,
9390 adapters_and_servers: &Vec<(Arc<CachedLspAdapter>, Arc<LanguageServer>)>,
9391 code_actions: Vec<lsp::CodeActionKind>,
9392 buffer: &Model<Buffer>,
9393 push_to_history: bool,
9394 project_transaction: &mut ProjectTransaction,
9395 cx: &mut AsyncAppContext,
9396 ) -> Result<(), anyhow::Error> {
9397 for (lsp_adapter, language_server) in adapters_and_servers.iter() {
9398 let code_actions = code_actions.clone();
9399
9400 let actions = project
9401 .update(cx, move |this, cx| {
9402 let request = GetCodeActions {
9403 range: text::Anchor::MIN..text::Anchor::MAX,
9404 kinds: Some(code_actions),
9405 };
9406 let server = LanguageServerToQuery::Other(language_server.server_id());
9407 this.request_lsp(buffer.clone(), server, request, cx)
9408 })?
9409 .await?;
9410
9411 for mut action in actions {
9412 Self::try_resolve_code_action(&language_server, &mut action)
9413 .await
9414 .context("resolving a formatting code action")?;
9415
9416 if let Some(edit) = action.lsp_action.edit {
9417 if edit.changes.is_none() && edit.document_changes.is_none() {
9418 continue;
9419 }
9420
9421 let new = Self::deserialize_workspace_edit(
9422 project
9423 .upgrade()
9424 .ok_or_else(|| anyhow!("project dropped"))?,
9425 edit,
9426 push_to_history,
9427 lsp_adapter.clone(),
9428 language_server.clone(),
9429 cx,
9430 )
9431 .await?;
9432 project_transaction.0.extend(new.0);
9433 }
9434
9435 if let Some(command) = action.lsp_action.command {
9436 project.update(cx, |this, _| {
9437 this.last_workspace_edits_by_language_server
9438 .remove(&language_server.server_id());
9439 })?;
9440
9441 language_server
9442 .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
9443 command: command.command,
9444 arguments: command.arguments.unwrap_or_default(),
9445 ..Default::default()
9446 })
9447 .await?;
9448
9449 project.update(cx, |this, _| {
9450 project_transaction.0.extend(
9451 this.last_workspace_edits_by_language_server
9452 .remove(&language_server.server_id())
9453 .unwrap_or_default()
9454 .0,
9455 )
9456 })?;
9457 }
9458 }
9459 }
9460
9461 Ok(())
9462 }
9463
9464 async fn handle_refresh_inlay_hints(
9465 this: Model<Self>,
9466 _: TypedEnvelope<proto::RefreshInlayHints>,
9467 mut cx: AsyncAppContext,
9468 ) -> Result<proto::Ack> {
9469 this.update(&mut cx, |_, cx| {
9470 cx.emit(Event::RefreshInlayHints);
9471 })?;
9472 Ok(proto::Ack {})
9473 }
9474
9475 async fn handle_lsp_command<T: LspCommand>(
9476 this: Model<Self>,
9477 envelope: TypedEnvelope<T::ProtoRequest>,
9478 mut cx: AsyncAppContext,
9479 ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
9480 where
9481 <T::LspRequest as lsp::request::Request>::Params: Send,
9482 <T::LspRequest as lsp::request::Request>::Result: Send,
9483 {
9484 let sender_id = envelope.original_sender_id()?;
9485 let buffer_id = T::buffer_id_from_proto(&envelope.payload)?;
9486 let buffer_handle = this.update(&mut cx, |this, cx| {
9487 this.buffer_store.read(cx).get_existing(buffer_id)
9488 })??;
9489 let request = T::from_proto(
9490 envelope.payload,
9491 this.clone(),
9492 buffer_handle.clone(),
9493 cx.clone(),
9494 )
9495 .await?;
9496 let response = this
9497 .update(&mut cx, |this, cx| {
9498 this.request_lsp(
9499 buffer_handle.clone(),
9500 LanguageServerToQuery::Primary,
9501 request,
9502 cx,
9503 )
9504 })?
9505 .await?;
9506 this.update(&mut cx, |this, cx| {
9507 Ok(T::response_to_proto(
9508 response,
9509 this,
9510 sender_id,
9511 &buffer_handle.read(cx).version(),
9512 cx,
9513 ))
9514 })?
9515 }
9516
9517 async fn handle_get_project_symbols(
9518 this: Model<Self>,
9519 envelope: TypedEnvelope<proto::GetProjectSymbols>,
9520 mut cx: AsyncAppContext,
9521 ) -> Result<proto::GetProjectSymbolsResponse> {
9522 let symbols = this
9523 .update(&mut cx, |this, cx| {
9524 this.symbols(&envelope.payload.query, cx)
9525 })?
9526 .await?;
9527
9528 Ok(proto::GetProjectSymbolsResponse {
9529 symbols: symbols.iter().map(serialize_symbol).collect(),
9530 })
9531 }
9532
9533 async fn handle_search_project(
9534 this: Model<Self>,
9535 envelope: TypedEnvelope<proto::SearchProject>,
9536 mut cx: AsyncAppContext,
9537 ) -> Result<proto::SearchProjectResponse> {
9538 let peer_id = envelope.original_sender_id()?;
9539 let query = SearchQuery::from_proto_v1(envelope.payload)?;
9540 let mut result = this.update(&mut cx, |this, cx| this.search(query, cx))?;
9541
9542 cx.spawn(move |mut cx| async move {
9543 let mut locations = Vec::new();
9544 let mut limit_reached = false;
9545 while let Some(result) = result.next().await {
9546 match result {
9547 SearchResult::Buffer { buffer, ranges } => {
9548 for range in ranges {
9549 let start = serialize_anchor(&range.start);
9550 let end = serialize_anchor(&range.end);
9551 let buffer_id = this.update(&mut cx, |this, cx| {
9552 this.create_buffer_for_peer(&buffer, peer_id, cx).into()
9553 })?;
9554 locations.push(proto::Location {
9555 buffer_id,
9556 start: Some(start),
9557 end: Some(end),
9558 });
9559 }
9560 }
9561 SearchResult::LimitReached => limit_reached = true,
9562 }
9563 }
9564 Ok(proto::SearchProjectResponse {
9565 locations,
9566 limit_reached,
9567 // will restart
9568 })
9569 })
9570 .await
9571 }
9572
9573 async fn handle_search_candidate_buffers(
9574 this: Model<Self>,
9575 envelope: TypedEnvelope<proto::FindSearchCandidates>,
9576 mut cx: AsyncAppContext,
9577 ) -> Result<proto::FindSearchCandidatesResponse> {
9578 let peer_id = envelope.original_sender_id()?;
9579 let message = envelope.payload;
9580 let query = SearchQuery::from_proto(
9581 message
9582 .query
9583 .ok_or_else(|| anyhow!("missing query field"))?,
9584 )?;
9585 let mut results = this.update(&mut cx, |this, cx| {
9586 this.search_for_candidate_buffers(&query, message.limit as _, cx)
9587 })?;
9588
9589 let mut response = proto::FindSearchCandidatesResponse {
9590 buffer_ids: Vec::new(),
9591 };
9592
9593 while let Some(buffer) = results.next().await {
9594 this.update(&mut cx, |this, cx| {
9595 let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
9596 response.buffer_ids.push(buffer_id.to_proto());
9597 })?;
9598 }
9599
9600 Ok(response)
9601 }
9602
9603 async fn handle_open_buffer_for_symbol(
9604 this: Model<Self>,
9605 envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
9606 mut cx: AsyncAppContext,
9607 ) -> Result<proto::OpenBufferForSymbolResponse> {
9608 let peer_id = envelope.original_sender_id()?;
9609 let symbol = envelope
9610 .payload
9611 .symbol
9612 .ok_or_else(|| anyhow!("invalid symbol"))?;
9613 let symbol = Self::deserialize_symbol(symbol)?;
9614 let symbol = this.update(&mut cx, |this, _| {
9615 let signature = this.symbol_signature(&symbol.path);
9616 if signature == symbol.signature {
9617 Ok(symbol)
9618 } else {
9619 Err(anyhow!("invalid symbol signature"))
9620 }
9621 })??;
9622 let buffer = this
9623 .update(&mut cx, |this, cx| {
9624 this.open_buffer_for_symbol(
9625 &Symbol {
9626 language_server_name: symbol.language_server_name,
9627 source_worktree_id: symbol.source_worktree_id,
9628 path: symbol.path,
9629 name: symbol.name,
9630 kind: symbol.kind,
9631 range: symbol.range,
9632 signature: symbol.signature,
9633 label: CodeLabel {
9634 text: Default::default(),
9635 runs: Default::default(),
9636 filter_range: Default::default(),
9637 },
9638 },
9639 cx,
9640 )
9641 })?
9642 .await?;
9643
9644 this.update(&mut cx, |this, cx| {
9645 let is_private = buffer
9646 .read(cx)
9647 .file()
9648 .map(|f| f.is_private())
9649 .unwrap_or_default();
9650 if is_private {
9651 Err(anyhow!(ErrorCode::UnsharedItem))
9652 } else {
9653 Ok(proto::OpenBufferForSymbolResponse {
9654 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
9655 })
9656 }
9657 })?
9658 }
9659
9660 fn symbol_signature(&self, project_path: &ProjectPath) -> [u8; 32] {
9661 let mut hasher = Sha256::new();
9662 hasher.update(project_path.worktree_id.to_proto().to_be_bytes());
9663 hasher.update(project_path.path.to_string_lossy().as_bytes());
9664 hasher.update(self.nonce.to_be_bytes());
9665 hasher.finalize().as_slice().try_into().unwrap()
9666 }
9667
9668 async fn handle_open_buffer_by_id(
9669 this: Model<Self>,
9670 envelope: TypedEnvelope<proto::OpenBufferById>,
9671 mut cx: AsyncAppContext,
9672 ) -> Result<proto::OpenBufferResponse> {
9673 let peer_id = envelope.original_sender_id()?;
9674 let buffer_id = BufferId::new(envelope.payload.id)?;
9675 let buffer = this
9676 .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
9677 .await?;
9678 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
9679 }
9680
9681 async fn handle_open_buffer_by_path(
9682 this: Model<Self>,
9683 envelope: TypedEnvelope<proto::OpenBufferByPath>,
9684 mut cx: AsyncAppContext,
9685 ) -> Result<proto::OpenBufferResponse> {
9686 let peer_id = envelope.original_sender_id()?;
9687 let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
9688 let open_buffer = this.update(&mut cx, |this, cx| {
9689 this.open_buffer(
9690 ProjectPath {
9691 worktree_id,
9692 path: PathBuf::from(envelope.payload.path).into(),
9693 },
9694 cx,
9695 )
9696 })?;
9697
9698 let buffer = open_buffer.await?;
9699 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
9700 }
9701
9702 async fn handle_open_new_buffer(
9703 this: Model<Self>,
9704 envelope: TypedEnvelope<proto::OpenNewBuffer>,
9705 mut cx: AsyncAppContext,
9706 ) -> Result<proto::OpenBufferResponse> {
9707 let buffer = this.update(&mut cx, |this, cx| this.create_local_buffer("", None, cx))?;
9708 let peer_id = envelope.original_sender_id()?;
9709
9710 Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
9711 }
9712
9713 fn respond_to_open_buffer_request(
9714 this: Model<Self>,
9715 buffer: Model<Buffer>,
9716 peer_id: proto::PeerId,
9717 cx: &mut AsyncAppContext,
9718 ) -> Result<proto::OpenBufferResponse> {
9719 this.update(cx, |this, cx| {
9720 let is_private = buffer
9721 .read(cx)
9722 .file()
9723 .map(|f| f.is_private())
9724 .unwrap_or_default();
9725 if is_private {
9726 Err(anyhow!(ErrorCode::UnsharedItem))
9727 } else {
9728 Ok(proto::OpenBufferResponse {
9729 buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
9730 })
9731 }
9732 })?
9733 }
9734
9735 fn serialize_project_transaction_for_peer(
9736 &mut self,
9737 project_transaction: ProjectTransaction,
9738 peer_id: proto::PeerId,
9739 cx: &mut AppContext,
9740 ) -> proto::ProjectTransaction {
9741 let mut serialized_transaction = proto::ProjectTransaction {
9742 buffer_ids: Default::default(),
9743 transactions: Default::default(),
9744 };
9745 for (buffer, transaction) in project_transaction.0 {
9746 serialized_transaction
9747 .buffer_ids
9748 .push(self.create_buffer_for_peer(&buffer, peer_id, cx).into());
9749 serialized_transaction
9750 .transactions
9751 .push(language::proto::serialize_transaction(&transaction));
9752 }
9753 serialized_transaction
9754 }
9755
9756 async fn deserialize_project_transaction(
9757 this: WeakModel<Self>,
9758 message: proto::ProjectTransaction,
9759 push_to_history: bool,
9760 mut cx: AsyncAppContext,
9761 ) -> Result<ProjectTransaction> {
9762 let mut project_transaction = ProjectTransaction::default();
9763 for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions) {
9764 let buffer_id = BufferId::new(buffer_id)?;
9765 let buffer = this
9766 .update(&mut cx, |this, cx| {
9767 this.wait_for_remote_buffer(buffer_id, cx)
9768 })?
9769 .await?;
9770 let transaction = language::proto::deserialize_transaction(transaction)?;
9771 project_transaction.0.insert(buffer, transaction);
9772 }
9773
9774 for (buffer, transaction) in &project_transaction.0 {
9775 buffer
9776 .update(&mut cx, |buffer, _| {
9777 buffer.wait_for_edits(transaction.edit_ids.iter().copied())
9778 })?
9779 .await?;
9780
9781 if push_to_history {
9782 buffer.update(&mut cx, |buffer, _| {
9783 buffer.push_transaction(transaction.clone(), Instant::now());
9784 })?;
9785 }
9786 }
9787
9788 Ok(project_transaction)
9789 }
9790
9791 fn create_buffer_for_peer(
9792 &mut self,
9793 buffer: &Model<Buffer>,
9794 peer_id: proto::PeerId,
9795 cx: &mut AppContext,
9796 ) -> BufferId {
9797 let buffer_id = buffer.read(cx).remote_id();
9798 if !self
9799 .shared_buffers
9800 .entry(peer_id)
9801 .or_default()
9802 .insert(buffer_id)
9803 {
9804 return buffer_id;
9805 }
9806 let ProjectClientState::Shared { remote_id } = self.client_state else {
9807 return buffer_id;
9808 };
9809 let buffer_store = self.buffer_store.clone();
9810 let client = self.client().clone();
9811
9812 cx.spawn(|mut cx| async move {
9813 BufferStore::create_buffer_for_peer(
9814 buffer_store,
9815 peer_id,
9816 buffer_id,
9817 remote_id,
9818 client.clone().into(),
9819 &mut cx,
9820 )
9821 .await?;
9822 anyhow::Ok(())
9823 })
9824 .detach_and_log_err(cx);
9825 buffer_id
9826 }
9827
9828 fn wait_for_remote_buffer(
9829 &mut self,
9830 id: BufferId,
9831 cx: &mut ModelContext<Self>,
9832 ) -> Task<Result<Model<Buffer>>> {
9833 self.buffer_store.update(cx, |buffer_store, cx| {
9834 buffer_store.wait_for_remote_buffer(id, cx)
9835 })
9836 }
9837
9838 fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
9839 let project_id = match self.client_state {
9840 ProjectClientState::Remote {
9841 sharing_has_stopped,
9842 remote_id,
9843 ..
9844 } => {
9845 if sharing_has_stopped {
9846 return Task::ready(Err(anyhow!(
9847 "can't synchronize remote buffers on a readonly project"
9848 )));
9849 } else {
9850 remote_id
9851 }
9852 }
9853 ProjectClientState::Shared { .. } | ProjectClientState::Local => {
9854 return Task::ready(Err(anyhow!(
9855 "can't synchronize remote buffers on a local project"
9856 )))
9857 }
9858 };
9859
9860 let client = self.client.clone();
9861 cx.spawn(move |this, mut cx| async move {
9862 let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
9863 this.buffer_store.read(cx).buffer_version_info(cx)
9864 })?;
9865 let response = client
9866 .request(proto::SynchronizeBuffers {
9867 project_id,
9868 buffers,
9869 })
9870 .await?;
9871
9872 let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
9873 response
9874 .buffers
9875 .into_iter()
9876 .map(|buffer| {
9877 let client = client.clone();
9878 let buffer_id = match BufferId::new(buffer.id) {
9879 Ok(id) => id,
9880 Err(e) => {
9881 return Task::ready(Err(e));
9882 }
9883 };
9884 let remote_version = language::proto::deserialize_version(&buffer.version);
9885 if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
9886 let operations =
9887 buffer.read(cx).serialize_ops(Some(remote_version), cx);
9888 cx.background_executor().spawn(async move {
9889 let operations = operations.await;
9890 for chunk in split_operations(operations) {
9891 client
9892 .request(proto::UpdateBuffer {
9893 project_id,
9894 buffer_id: buffer_id.into(),
9895 operations: chunk,
9896 })
9897 .await?;
9898 }
9899 anyhow::Ok(())
9900 })
9901 } else {
9902 Task::ready(Ok(()))
9903 }
9904 })
9905 .collect::<Vec<_>>()
9906 })?;
9907
9908 // Any incomplete buffers have open requests waiting. Request that the host sends
9909 // creates these buffers for us again to unblock any waiting futures.
9910 for id in incomplete_buffer_ids {
9911 cx.background_executor()
9912 .spawn(client.request(proto::OpenBufferById {
9913 project_id,
9914 id: id.into(),
9915 }))
9916 .detach();
9917 }
9918
9919 futures::future::join_all(send_updates_for_buffers)
9920 .await
9921 .into_iter()
9922 .collect()
9923 })
9924 }
9925
9926 pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
9927 self.worktrees(cx)
9928 .map(|worktree| {
9929 let worktree = worktree.read(cx);
9930 proto::WorktreeMetadata {
9931 id: worktree.id().to_proto(),
9932 root_name: worktree.root_name().into(),
9933 visible: worktree.is_visible(),
9934 abs_path: worktree.abs_path().to_string_lossy().into(),
9935 }
9936 })
9937 .collect()
9938 }
9939
9940 fn set_worktrees_from_proto(
9941 &mut self,
9942 worktrees: Vec<proto::WorktreeMetadata>,
9943 cx: &mut ModelContext<Project>,
9944 ) -> Result<()> {
9945 self.metadata_changed(cx);
9946 self.worktree_store.update(cx, |worktree_store, cx| {
9947 worktree_store.set_worktrees_from_proto(
9948 worktrees,
9949 self.replica_id(),
9950 self.remote_id().ok_or_else(|| anyhow!("invalid project"))?,
9951 self.client.clone().into(),
9952 cx,
9953 )
9954 })
9955 }
9956
9957 fn set_collaborators_from_proto(
9958 &mut self,
9959 messages: Vec<proto::Collaborator>,
9960 cx: &mut ModelContext<Self>,
9961 ) -> Result<()> {
9962 let mut collaborators = HashMap::default();
9963 for message in messages {
9964 let collaborator = Collaborator::from_proto(message)?;
9965 collaborators.insert(collaborator.peer_id, collaborator);
9966 }
9967 for old_peer_id in self.collaborators.keys() {
9968 if !collaborators.contains_key(old_peer_id) {
9969 cx.emit(Event::CollaboratorLeft(*old_peer_id));
9970 }
9971 }
9972 self.collaborators = collaborators;
9973 Ok(())
9974 }
9975
9976 fn deserialize_symbol(serialized_symbol: proto::Symbol) -> Result<CoreSymbol> {
9977 let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
9978 let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
9979 let kind = unsafe { mem::transmute::<i32, lsp::SymbolKind>(serialized_symbol.kind) };
9980 let path = ProjectPath {
9981 worktree_id,
9982 path: PathBuf::from(serialized_symbol.path).into(),
9983 };
9984
9985 let start = serialized_symbol
9986 .start
9987 .ok_or_else(|| anyhow!("invalid start"))?;
9988 let end = serialized_symbol
9989 .end
9990 .ok_or_else(|| anyhow!("invalid end"))?;
9991 Ok(CoreSymbol {
9992 language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
9993 source_worktree_id,
9994 path,
9995 name: serialized_symbol.name,
9996 range: Unclipped(PointUtf16::new(start.row, start.column))
9997 ..Unclipped(PointUtf16::new(end.row, end.column)),
9998 kind,
9999 signature: serialized_symbol
10000 .signature
10001 .try_into()
10002 .map_err(|_| anyhow!("invalid signature"))?,
10003 })
10004 }
10005
10006 fn serialize_completion(completion: &CoreCompletion) -> proto::Completion {
10007 proto::Completion {
10008 old_start: Some(serialize_anchor(&completion.old_range.start)),
10009 old_end: Some(serialize_anchor(&completion.old_range.end)),
10010 new_text: completion.new_text.clone(),
10011 server_id: completion.server_id.0 as u64,
10012 lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(),
10013 }
10014 }
10015
10016 fn deserialize_completion(completion: proto::Completion) -> Result<CoreCompletion> {
10017 let old_start = completion
10018 .old_start
10019 .and_then(deserialize_anchor)
10020 .ok_or_else(|| anyhow!("invalid old start"))?;
10021 let old_end = completion
10022 .old_end
10023 .and_then(deserialize_anchor)
10024 .ok_or_else(|| anyhow!("invalid old end"))?;
10025 let lsp_completion = serde_json::from_slice(&completion.lsp_completion)?;
10026
10027 Ok(CoreCompletion {
10028 old_range: old_start..old_end,
10029 new_text: completion.new_text,
10030 server_id: LanguageServerId(completion.server_id as usize),
10031 lsp_completion,
10032 })
10033 }
10034
10035 fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
10036 proto::CodeAction {
10037 server_id: action.server_id.0 as u64,
10038 start: Some(serialize_anchor(&action.range.start)),
10039 end: Some(serialize_anchor(&action.range.end)),
10040 lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
10041 }
10042 }
10043
10044 fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
10045 let start = action
10046 .start
10047 .and_then(deserialize_anchor)
10048 .ok_or_else(|| anyhow!("invalid start"))?;
10049 let end = action
10050 .end
10051 .and_then(deserialize_anchor)
10052 .ok_or_else(|| anyhow!("invalid end"))?;
10053 let lsp_action = serde_json::from_slice(&action.lsp_action)?;
10054 Ok(CodeAction {
10055 server_id: LanguageServerId(action.server_id as usize),
10056 range: start..end,
10057 lsp_action,
10058 })
10059 }
10060
10061 #[allow(clippy::type_complexity)]
10062 fn edits_from_lsp(
10063 &mut self,
10064 buffer: &Model<Buffer>,
10065 lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
10066 server_id: LanguageServerId,
10067 version: Option<i32>,
10068 cx: &mut ModelContext<Self>,
10069 ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
10070 let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx);
10071 cx.background_executor().spawn(async move {
10072 let snapshot = snapshot?;
10073 let mut lsp_edits = lsp_edits
10074 .into_iter()
10075 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
10076 .collect::<Vec<_>>();
10077 lsp_edits.sort_by_key(|(range, _)| range.start);
10078
10079 let mut lsp_edits = lsp_edits.into_iter().peekable();
10080 let mut edits = Vec::new();
10081 while let Some((range, mut new_text)) = lsp_edits.next() {
10082 // Clip invalid ranges provided by the language server.
10083 let mut range = snapshot.clip_point_utf16(range.start, Bias::Left)
10084 ..snapshot.clip_point_utf16(range.end, Bias::Left);
10085
10086 // Combine any LSP edits that are adjacent.
10087 //
10088 // Also, combine LSP edits that are separated from each other by only
10089 // a newline. This is important because for some code actions,
10090 // Rust-analyzer rewrites the entire buffer via a series of edits that
10091 // are separated by unchanged newline characters.
10092 //
10093 // In order for the diffing logic below to work properly, any edits that
10094 // cancel each other out must be combined into one.
10095 while let Some((next_range, next_text)) = lsp_edits.peek() {
10096 if next_range.start.0 > range.end {
10097 if next_range.start.0.row > range.end.row + 1
10098 || next_range.start.0.column > 0
10099 || snapshot.clip_point_utf16(
10100 Unclipped(PointUtf16::new(range.end.row, u32::MAX)),
10101 Bias::Left,
10102 ) > range.end
10103 {
10104 break;
10105 }
10106 new_text.push('\n');
10107 }
10108 range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left);
10109 new_text.push_str(next_text);
10110 lsp_edits.next();
10111 }
10112
10113 // For multiline edits, perform a diff of the old and new text so that
10114 // we can identify the changes more precisely, preserving the locations
10115 // of any anchors positioned in the unchanged regions.
10116 if range.end.row > range.start.row {
10117 let mut offset = range.start.to_offset(&snapshot);
10118 let old_text = snapshot.text_for_range(range).collect::<String>();
10119
10120 let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
10121 let mut moved_since_edit = true;
10122 for change in diff.iter_all_changes() {
10123 let tag = change.tag();
10124 let value = change.value();
10125 match tag {
10126 ChangeTag::Equal => {
10127 offset += value.len();
10128 moved_since_edit = true;
10129 }
10130 ChangeTag::Delete => {
10131 let start = snapshot.anchor_after(offset);
10132 let end = snapshot.anchor_before(offset + value.len());
10133 if moved_since_edit {
10134 edits.push((start..end, String::new()));
10135 } else {
10136 edits.last_mut().unwrap().0.end = end;
10137 }
10138 offset += value.len();
10139 moved_since_edit = false;
10140 }
10141 ChangeTag::Insert => {
10142 if moved_since_edit {
10143 let anchor = snapshot.anchor_after(offset);
10144 edits.push((anchor..anchor, value.to_string()));
10145 } else {
10146 edits.last_mut().unwrap().1.push_str(value);
10147 }
10148 moved_since_edit = false;
10149 }
10150 }
10151 }
10152 } else if range.end == range.start {
10153 let anchor = snapshot.anchor_after(range.start);
10154 edits.push((anchor..anchor, new_text));
10155 } else {
10156 let edit_start = snapshot.anchor_after(range.start);
10157 let edit_end = snapshot.anchor_before(range.end);
10158 edits.push((edit_start..edit_end, new_text));
10159 }
10160 }
10161
10162 Ok(edits)
10163 })
10164 }
10165
10166 fn buffer_snapshot_for_lsp_version(
10167 &mut self,
10168 buffer: &Model<Buffer>,
10169 server_id: LanguageServerId,
10170 version: Option<i32>,
10171 cx: &AppContext,
10172 ) -> Result<TextBufferSnapshot> {
10173 const OLD_VERSIONS_TO_RETAIN: i32 = 10;
10174
10175 if let Some(version) = version {
10176 let buffer_id = buffer.read(cx).remote_id();
10177 let snapshots = self
10178 .buffer_snapshots
10179 .get_mut(&buffer_id)
10180 .and_then(|m| m.get_mut(&server_id))
10181 .ok_or_else(|| {
10182 anyhow!("no snapshots found for buffer {buffer_id} and server {server_id}")
10183 })?;
10184
10185 let found_snapshot = snapshots
10186 .binary_search_by_key(&version, |e| e.version)
10187 .map(|ix| snapshots[ix].snapshot.clone())
10188 .map_err(|_| {
10189 anyhow!("snapshot not found for buffer {buffer_id} server {server_id} at version {version}")
10190 })?;
10191
10192 snapshots.retain(|snapshot| snapshot.version + OLD_VERSIONS_TO_RETAIN >= version);
10193 Ok(found_snapshot)
10194 } else {
10195 Ok((buffer.read(cx)).text_snapshot())
10196 }
10197 }
10198
10199 pub fn language_servers(
10200 &self,
10201 ) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
10202 self.language_server_ids
10203 .iter()
10204 .map(|((worktree_id, server_name), server_id)| {
10205 (*server_id, server_name.clone(), *worktree_id)
10206 })
10207 }
10208
10209 pub fn supplementary_language_servers(
10210 &self,
10211 ) -> impl '_ + Iterator<Item = (&LanguageServerId, &LanguageServerName)> {
10212 self.supplementary_language_servers
10213 .iter()
10214 .map(|(id, (name, _))| (id, name))
10215 }
10216
10217 pub fn language_server_adapter_for_id(
10218 &self,
10219 id: LanguageServerId,
10220 ) -> Option<Arc<CachedLspAdapter>> {
10221 if let Some(LanguageServerState::Running { adapter, .. }) = self.language_servers.get(&id) {
10222 Some(adapter.clone())
10223 } else {
10224 None
10225 }
10226 }
10227
10228 pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
10229 if let Some(LanguageServerState::Running { server, .. }) = self.language_servers.get(&id) {
10230 Some(server.clone())
10231 } else if let Some((_, server)) = self.supplementary_language_servers.get(&id) {
10232 Some(Arc::clone(server))
10233 } else {
10234 None
10235 }
10236 }
10237
10238 pub fn language_servers_for_buffer(
10239 &self,
10240 buffer: &Buffer,
10241 cx: &AppContext,
10242 ) -> impl Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
10243 self.language_server_ids_for_buffer(buffer, cx)
10244 .into_iter()
10245 .filter_map(|server_id| match self.language_servers.get(&server_id)? {
10246 LanguageServerState::Running {
10247 adapter, server, ..
10248 } => Some((adapter, server)),
10249 _ => None,
10250 })
10251 }
10252
10253 fn primary_language_server_for_buffer(
10254 &self,
10255 buffer: &Buffer,
10256 cx: &AppContext,
10257 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
10258 // The list of language servers is ordered based on the `language_servers` setting
10259 // for each language, thus we can consider the first one in the list to be the
10260 // primary one.
10261 self.language_servers_for_buffer(buffer, cx).next()
10262 }
10263
10264 pub fn language_server_for_buffer(
10265 &self,
10266 buffer: &Buffer,
10267 server_id: LanguageServerId,
10268 cx: &AppContext,
10269 ) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
10270 self.language_servers_for_buffer(buffer, cx)
10271 .find(|(_, s)| s.server_id() == server_id)
10272 }
10273
10274 fn language_server_ids_for_buffer(
10275 &self,
10276 buffer: &Buffer,
10277 cx: &AppContext,
10278 ) -> Vec<LanguageServerId> {
10279 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
10280 let worktree_id = file.worktree_id(cx);
10281 self.languages
10282 .lsp_adapters(&language)
10283 .iter()
10284 .flat_map(|adapter| {
10285 let key = (worktree_id, adapter.name.clone());
10286 self.language_server_ids.get(&key).copied()
10287 })
10288 .collect()
10289 } else {
10290 Vec::new()
10291 }
10292 }
10293
10294 pub fn task_context_for_location(
10295 &self,
10296 captured_variables: TaskVariables,
10297 location: Location,
10298 cx: &mut ModelContext<'_, Project>,
10299 ) -> Task<Option<TaskContext>> {
10300 if self.is_local_or_ssh() {
10301 let (worktree_id, cwd) = if let Some(worktree) = self.task_worktree(cx) {
10302 (
10303 Some(worktree.read(cx).id()),
10304 Some(self.task_cwd(worktree, cx)),
10305 )
10306 } else {
10307 (None, None)
10308 };
10309
10310 cx.spawn(|project, cx| async move {
10311 let mut task_variables = cx
10312 .update(|cx| {
10313 combine_task_variables(
10314 captured_variables,
10315 location,
10316 BasicContextProvider::new(project.upgrade()?),
10317 cx,
10318 )
10319 .log_err()
10320 })
10321 .ok()
10322 .flatten()?;
10323 // Remove all custom entries starting with _, as they're not intended for use by the end user.
10324 task_variables.sweep();
10325
10326 let mut project_env = None;
10327 if let Some((worktree_id, cwd)) = worktree_id.zip(cwd.as_ref()) {
10328 let env = Self::get_worktree_shell_env(project, worktree_id, cwd, cx).await;
10329 if let Some(env) = env {
10330 project_env.replace(env);
10331 }
10332 };
10333
10334 Some(TaskContext {
10335 project_env: project_env.unwrap_or_default(),
10336 cwd,
10337 task_variables,
10338 })
10339 })
10340 } else if let Some(project_id) = self
10341 .remote_id()
10342 .filter(|_| self.ssh_connection_string(cx).is_some())
10343 {
10344 let task_context = self.client().request(proto::TaskContextForLocation {
10345 project_id,
10346 location: Some(proto::Location {
10347 buffer_id: location.buffer.read(cx).remote_id().into(),
10348 start: Some(serialize_anchor(&location.range.start)),
10349 end: Some(serialize_anchor(&location.range.end)),
10350 }),
10351 });
10352 cx.background_executor().spawn(async move {
10353 let task_context = task_context.await.log_err()?;
10354 Some(TaskContext {
10355 project_env: task_context.project_env.into_iter().collect(),
10356 cwd: task_context.cwd.map(PathBuf::from),
10357 task_variables: task_context
10358 .task_variables
10359 .into_iter()
10360 .filter_map(
10361 |(variable_name, variable_value)| match variable_name.parse() {
10362 Ok(variable_name) => Some((variable_name, variable_value)),
10363 Err(()) => {
10364 log::error!("Unknown variable name: {variable_name}");
10365 None
10366 }
10367 },
10368 )
10369 .collect(),
10370 })
10371 })
10372 } else {
10373 Task::ready(None)
10374 }
10375 }
10376
10377 async fn get_worktree_shell_env(
10378 this: WeakModel<Self>,
10379 worktree_id: WorktreeId,
10380 cwd: &PathBuf,
10381 mut cx: AsyncAppContext,
10382 ) -> Option<HashMap<String, String>> {
10383 let cached_env = this
10384 .update(&mut cx, |project, _| {
10385 project.cached_shell_environments.get(&worktree_id).cloned()
10386 })
10387 .ok()?;
10388
10389 if let Some(env) = cached_env {
10390 Some(env)
10391 } else {
10392 let load_direnv = this
10393 .update(&mut cx, |_, cx| {
10394 ProjectSettings::get_global(cx).load_direnv.clone()
10395 })
10396 .ok()?;
10397
10398 let shell_env = cx
10399 .background_executor()
10400 .spawn({
10401 let cwd = cwd.clone();
10402 async move {
10403 load_shell_environment(&cwd, &load_direnv)
10404 .await
10405 .unwrap_or_default()
10406 }
10407 })
10408 .await;
10409
10410 this.update(&mut cx, |project, _| {
10411 project
10412 .cached_shell_environments
10413 .insert(worktree_id, shell_env.clone());
10414 })
10415 .ok()?;
10416
10417 Some(shell_env)
10418 }
10419 }
10420
10421 pub fn task_templates(
10422 &self,
10423 worktree: Option<WorktreeId>,
10424 location: Option<Location>,
10425 cx: &mut ModelContext<Self>,
10426 ) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
10427 if self.is_local_or_ssh() {
10428 let (file, language) = location
10429 .map(|location| {
10430 let buffer = location.buffer.read(cx);
10431 (
10432 buffer.file().cloned(),
10433 buffer.language_at(location.range.start),
10434 )
10435 })
10436 .unwrap_or_default();
10437 Task::ready(Ok(self
10438 .task_inventory()
10439 .read(cx)
10440 .list_tasks(file, language, worktree, cx)))
10441 } else if let Some(project_id) = self
10442 .remote_id()
10443 .filter(|_| self.ssh_connection_string(cx).is_some())
10444 {
10445 let remote_templates =
10446 self.query_remote_task_templates(project_id, worktree, location.as_ref(), cx);
10447 cx.background_executor().spawn(remote_templates)
10448 } else {
10449 Task::ready(Ok(Vec::new()))
10450 }
10451 }
10452
10453 pub fn query_remote_task_templates(
10454 &self,
10455 project_id: u64,
10456 worktree: Option<WorktreeId>,
10457 location: Option<&Location>,
10458 cx: &AppContext,
10459 ) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
10460 let client = self.client();
10461 let location = location.map(|location| serialize_location(location, cx));
10462 cx.spawn(|_| async move {
10463 let response = client
10464 .request(proto::TaskTemplates {
10465 project_id,
10466 worktree_id: worktree.map(|id| id.to_proto()),
10467 location,
10468 })
10469 .await?;
10470
10471 Ok(response
10472 .templates
10473 .into_iter()
10474 .filter_map(|template_pair| {
10475 let task_source_kind = match template_pair.kind?.kind? {
10476 proto::task_source_kind::Kind::UserInput(_) => TaskSourceKind::UserInput,
10477 proto::task_source_kind::Kind::Worktree(worktree) => {
10478 TaskSourceKind::Worktree {
10479 id: WorktreeId::from_proto(worktree.id),
10480 abs_path: PathBuf::from(worktree.abs_path),
10481 id_base: Cow::Owned(worktree.id_base),
10482 }
10483 }
10484 proto::task_source_kind::Kind::AbsPath(abs_path) => {
10485 TaskSourceKind::AbsPath {
10486 id_base: Cow::Owned(abs_path.id_base),
10487 abs_path: PathBuf::from(abs_path.abs_path),
10488 }
10489 }
10490 proto::task_source_kind::Kind::Language(language) => {
10491 TaskSourceKind::Language {
10492 name: language.name.into(),
10493 }
10494 }
10495 };
10496
10497 let proto_template = template_pair.template?;
10498 let reveal = match proto::RevealStrategy::from_i32(proto_template.reveal)
10499 .unwrap_or(proto::RevealStrategy::RevealAlways)
10500 {
10501 proto::RevealStrategy::RevealAlways => RevealStrategy::Always,
10502 proto::RevealStrategy::RevealNever => RevealStrategy::Never,
10503 };
10504 let hide = match proto::HideStrategy::from_i32(proto_template.hide)
10505 .unwrap_or(proto::HideStrategy::HideNever)
10506 {
10507 proto::HideStrategy::HideAlways => HideStrategy::Always,
10508 proto::HideStrategy::HideNever => HideStrategy::Never,
10509 proto::HideStrategy::HideOnSuccess => HideStrategy::OnSuccess,
10510 };
10511 let shell = match proto_template
10512 .shell
10513 .and_then(|shell| shell.shell_type)
10514 .unwrap_or(proto::shell::ShellType::System(proto::System {}))
10515 {
10516 proto::shell::ShellType::System(_) => Shell::System,
10517 proto::shell::ShellType::Program(program) => Shell::Program(program),
10518 proto::shell::ShellType::WithArguments(with_arguments) => {
10519 Shell::WithArguments {
10520 program: with_arguments.program,
10521 args: with_arguments.args,
10522 }
10523 }
10524 };
10525 let task_template = TaskTemplate {
10526 label: proto_template.label,
10527 command: proto_template.command,
10528 args: proto_template.args,
10529 env: proto_template.env.into_iter().collect(),
10530 cwd: proto_template.cwd,
10531 use_new_terminal: proto_template.use_new_terminal,
10532 allow_concurrent_runs: proto_template.allow_concurrent_runs,
10533 reveal,
10534 hide,
10535 shell,
10536 tags: proto_template.tags,
10537 };
10538 Some((task_source_kind, task_template))
10539 })
10540 .collect())
10541 })
10542 }
10543
10544 fn task_worktree(&self, cx: &AppContext) -> Option<Model<Worktree>> {
10545 let available_worktrees = self
10546 .worktrees(cx)
10547 .filter(|worktree| {
10548 let worktree = worktree.read(cx);
10549 worktree.is_visible()
10550 && worktree.is_local()
10551 && worktree.root_entry().map_or(false, |e| e.is_dir())
10552 })
10553 .collect::<Vec<_>>();
10554
10555 match available_worktrees.len() {
10556 0 => None,
10557 1 => Some(available_worktrees[0].clone()),
10558 _ => self.active_entry().and_then(|entry_id| {
10559 available_worktrees.into_iter().find_map(|worktree| {
10560 if worktree.read(cx).contains_entry(entry_id) {
10561 Some(worktree)
10562 } else {
10563 None
10564 }
10565 })
10566 }),
10567 }
10568 }
10569
10570 fn task_cwd(&self, worktree: Model<Worktree>, cx: &AppContext) -> PathBuf {
10571 worktree.read(cx).abs_path().to_path_buf()
10572 }
10573}
10574
10575fn combine_task_variables(
10576 mut captured_variables: TaskVariables,
10577 location: Location,
10578 baseline: BasicContextProvider,
10579 cx: &mut AppContext,
10580) -> anyhow::Result<TaskVariables> {
10581 let language_context_provider = location
10582 .buffer
10583 .read(cx)
10584 .language()
10585 .and_then(|language| language.context_provider());
10586 let baseline = baseline
10587 .build_context(&captured_variables, &location, cx)
10588 .context("building basic default context")?;
10589 captured_variables.extend(baseline);
10590 if let Some(provider) = language_context_provider {
10591 captured_variables.extend(
10592 provider
10593 .build_context(&captured_variables, &location, cx)
10594 .context("building provider context")?,
10595 );
10596 }
10597 Ok(captured_variables)
10598}
10599
10600async fn populate_labels_for_symbols(
10601 symbols: Vec<CoreSymbol>,
10602 language_registry: &Arc<LanguageRegistry>,
10603 default_language: Option<Arc<Language>>,
10604 lsp_adapter: Option<Arc<CachedLspAdapter>>,
10605 output: &mut Vec<Symbol>,
10606) {
10607 #[allow(clippy::mutable_key_type)]
10608 let mut symbols_by_language = HashMap::<Option<Arc<Language>>, Vec<CoreSymbol>>::default();
10609
10610 let mut unknown_path = None;
10611 for symbol in symbols {
10612 let language = language_registry
10613 .language_for_file_path(&symbol.path.path)
10614 .await
10615 .ok()
10616 .or_else(|| {
10617 unknown_path.get_or_insert(symbol.path.path.clone());
10618 default_language.clone()
10619 });
10620 symbols_by_language
10621 .entry(language)
10622 .or_default()
10623 .push(symbol);
10624 }
10625
10626 if let Some(unknown_path) = unknown_path {
10627 log::info!(
10628 "no language found for symbol path {}",
10629 unknown_path.display()
10630 );
10631 }
10632
10633 let mut label_params = Vec::new();
10634 for (language, mut symbols) in symbols_by_language {
10635 label_params.clear();
10636 label_params.extend(
10637 symbols
10638 .iter_mut()
10639 .map(|symbol| (mem::take(&mut symbol.name), symbol.kind)),
10640 );
10641
10642 let mut labels = Vec::new();
10643 if let Some(language) = language {
10644 let lsp_adapter = lsp_adapter
10645 .clone()
10646 .or_else(|| language_registry.lsp_adapters(&language).first().cloned());
10647 if let Some(lsp_adapter) = lsp_adapter {
10648 labels = lsp_adapter
10649 .labels_for_symbols(&label_params, &language)
10650 .await
10651 .log_err()
10652 .unwrap_or_default();
10653 }
10654 }
10655
10656 for ((symbol, (name, _)), label) in symbols
10657 .into_iter()
10658 .zip(label_params.drain(..))
10659 .zip(labels.into_iter().chain(iter::repeat(None)))
10660 {
10661 output.push(Symbol {
10662 language_server_name: symbol.language_server_name,
10663 source_worktree_id: symbol.source_worktree_id,
10664 path: symbol.path,
10665 label: label.unwrap_or_else(|| CodeLabel::plain(name.clone(), None)),
10666 name,
10667 kind: symbol.kind,
10668 range: symbol.range,
10669 signature: symbol.signature,
10670 });
10671 }
10672 }
10673}
10674
10675async fn populate_labels_for_completions(
10676 mut new_completions: Vec<CoreCompletion>,
10677 language_registry: &Arc<LanguageRegistry>,
10678 language: Option<Arc<Language>>,
10679 lsp_adapter: Option<Arc<CachedLspAdapter>>,
10680 completions: &mut Vec<Completion>,
10681) {
10682 let lsp_completions = new_completions
10683 .iter_mut()
10684 .map(|completion| mem::take(&mut completion.lsp_completion))
10685 .collect::<Vec<_>>();
10686
10687 let labels = if let Some((language, lsp_adapter)) = language.as_ref().zip(lsp_adapter) {
10688 lsp_adapter
10689 .labels_for_completions(&lsp_completions, language)
10690 .await
10691 .log_err()
10692 .unwrap_or_default()
10693 } else {
10694 Vec::new()
10695 };
10696
10697 for ((completion, lsp_completion), label) in new_completions
10698 .into_iter()
10699 .zip(lsp_completions)
10700 .zip(labels.into_iter().chain(iter::repeat(None)))
10701 {
10702 let documentation = if let Some(docs) = &lsp_completion.documentation {
10703 Some(prepare_completion_documentation(docs, &language_registry, language.clone()).await)
10704 } else {
10705 None
10706 };
10707
10708 completions.push(Completion {
10709 old_range: completion.old_range,
10710 new_text: completion.new_text,
10711 label: label.unwrap_or_else(|| {
10712 CodeLabel::plain(
10713 lsp_completion.label.clone(),
10714 lsp_completion.filter_text.as_deref(),
10715 )
10716 }),
10717 server_id: completion.server_id,
10718 documentation,
10719 lsp_completion,
10720 confirm: None,
10721 })
10722 }
10723}
10724
10725fn deserialize_code_actions(code_actions: &HashMap<String, bool>) -> Vec<lsp::CodeActionKind> {
10726 code_actions
10727 .iter()
10728 .flat_map(|(kind, enabled)| {
10729 if *enabled {
10730 Some(kind.clone().into())
10731 } else {
10732 None
10733 }
10734 })
10735 .collect()
10736}
10737
10738fn glob_literal_prefix(glob: &str) -> &str {
10739 let mut literal_end = 0;
10740 for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() {
10741 if part.contains(&['*', '?', '{', '}']) {
10742 break;
10743 } else {
10744 if i > 0 {
10745 // Account for separator prior to this part
10746 literal_end += path::MAIN_SEPARATOR.len_utf8();
10747 }
10748 literal_end += part.len();
10749 }
10750 }
10751 &glob[..literal_end]
10752}
10753
10754pub struct PathMatchCandidateSet {
10755 pub snapshot: Snapshot,
10756 pub include_ignored: bool,
10757 pub include_root_name: bool,
10758 pub candidates: Candidates,
10759}
10760
10761pub enum Candidates {
10762 /// Only consider directories.
10763 Directories,
10764 /// Only consider files.
10765 Files,
10766 /// Consider directories and files.
10767 Entries,
10768}
10769
10770impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
10771 type Candidates = PathMatchCandidateSetIter<'a>;
10772
10773 fn id(&self) -> usize {
10774 self.snapshot.id().to_usize()
10775 }
10776
10777 fn len(&self) -> usize {
10778 match self.candidates {
10779 Candidates::Files => {
10780 if self.include_ignored {
10781 self.snapshot.file_count()
10782 } else {
10783 self.snapshot.visible_file_count()
10784 }
10785 }
10786
10787 Candidates::Directories => {
10788 if self.include_ignored {
10789 self.snapshot.dir_count()
10790 } else {
10791 self.snapshot.visible_dir_count()
10792 }
10793 }
10794
10795 Candidates::Entries => {
10796 if self.include_ignored {
10797 self.snapshot.entry_count()
10798 } else {
10799 self.snapshot.visible_entry_count()
10800 }
10801 }
10802 }
10803 }
10804
10805 fn prefix(&self) -> Arc<str> {
10806 if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
10807 self.snapshot.root_name().into()
10808 } else if self.include_root_name {
10809 format!("{}/", self.snapshot.root_name()).into()
10810 } else {
10811 Arc::default()
10812 }
10813 }
10814
10815 fn candidates(&'a self, start: usize) -> Self::Candidates {
10816 PathMatchCandidateSetIter {
10817 traversal: match self.candidates {
10818 Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
10819 Candidates::Files => self.snapshot.files(self.include_ignored, start),
10820 Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
10821 },
10822 }
10823 }
10824}
10825
10826pub struct PathMatchCandidateSetIter<'a> {
10827 traversal: Traversal<'a>,
10828}
10829
10830impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
10831 type Item = fuzzy::PathMatchCandidate<'a>;
10832
10833 fn next(&mut self) -> Option<Self::Item> {
10834 self.traversal
10835 .next()
10836 .map(|entry| fuzzy::PathMatchCandidate {
10837 is_dir: entry.kind.is_dir(),
10838 path: &entry.path,
10839 char_bag: entry.char_bag,
10840 })
10841 }
10842}
10843
10844impl EventEmitter<Event> for Project {}
10845
10846impl<'a> Into<SettingsLocation<'a>> for &'a ProjectPath {
10847 fn into(self) -> SettingsLocation<'a> {
10848 SettingsLocation {
10849 worktree_id: self.worktree_id.to_usize(),
10850 path: self.path.as_ref(),
10851 }
10852 }
10853}
10854
10855impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
10856 fn from((worktree_id, path): (WorktreeId, P)) -> Self {
10857 Self {
10858 worktree_id,
10859 path: path.as_ref().into(),
10860 }
10861 }
10862}
10863
10864pub struct ProjectLspAdapterDelegate {
10865 project: WeakModel<Project>,
10866 worktree: worktree::Snapshot,
10867 fs: Arc<dyn Fs>,
10868 http_client: Arc<dyn HttpClient>,
10869 language_registry: Arc<LanguageRegistry>,
10870 shell_env: Mutex<Option<HashMap<String, String>>>,
10871 load_direnv: DirenvSettings,
10872}
10873
10874impl ProjectLspAdapterDelegate {
10875 pub fn new(
10876 project: &Project,
10877 worktree: &Model<Worktree>,
10878 cx: &ModelContext<Project>,
10879 ) -> Arc<Self> {
10880 let load_direnv = ProjectSettings::get_global(cx).load_direnv.clone();
10881 Arc::new(Self {
10882 project: cx.weak_model(),
10883 worktree: worktree.read(cx).snapshot(),
10884 fs: project.fs.clone(),
10885 http_client: project.client.http_client(),
10886 language_registry: project.languages.clone(),
10887 shell_env: Default::default(),
10888 load_direnv,
10889 })
10890 }
10891
10892 async fn load_shell_env(&self) {
10893 let worktree_abs_path = self.worktree.abs_path();
10894 let shell_env = load_shell_environment(&worktree_abs_path, &self.load_direnv)
10895 .await
10896 .with_context(|| {
10897 format!("failed to determine load login shell environment in {worktree_abs_path:?}")
10898 })
10899 .log_err()
10900 .unwrap_or_default();
10901 *self.shell_env.lock() = Some(shell_env);
10902 }
10903}
10904
10905#[async_trait]
10906impl LspAdapterDelegate for ProjectLspAdapterDelegate {
10907 fn show_notification(&self, message: &str, cx: &mut AppContext) {
10908 self.project
10909 .update(cx, |_, cx| cx.emit(Event::Notification(message.to_owned())))
10910 .ok();
10911 }
10912
10913 fn http_client(&self) -> Arc<dyn HttpClient> {
10914 self.http_client.clone()
10915 }
10916
10917 fn worktree_id(&self) -> u64 {
10918 self.worktree.id().to_proto()
10919 }
10920
10921 fn worktree_root_path(&self) -> &Path {
10922 self.worktree.abs_path().as_ref()
10923 }
10924
10925 async fn shell_env(&self) -> HashMap<String, String> {
10926 self.load_shell_env().await;
10927 self.shell_env.lock().as_ref().cloned().unwrap_or_default()
10928 }
10929
10930 #[cfg(not(target_os = "windows"))]
10931 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
10932 let worktree_abs_path = self.worktree.abs_path();
10933 self.load_shell_env().await;
10934 let shell_path = self
10935 .shell_env
10936 .lock()
10937 .as_ref()
10938 .and_then(|shell_env| shell_env.get("PATH").cloned());
10939 which::which_in(command, shell_path.as_ref(), &worktree_abs_path).ok()
10940 }
10941
10942 #[cfg(target_os = "windows")]
10943 async fn which(&self, command: &OsStr) -> Option<PathBuf> {
10944 // todo(windows) Getting the shell env variables in a current directory on Windows is more complicated than other platforms
10945 // there isn't a 'default shell' necessarily. The closest would be the default profile on the windows terminal
10946 // SEE: https://learn.microsoft.com/en-us/windows/terminal/customize-settings/startup
10947 which::which(command).ok()
10948 }
10949
10950 fn update_status(
10951 &self,
10952 server_name: LanguageServerName,
10953 status: language::LanguageServerBinaryStatus,
10954 ) {
10955 self.language_registry
10956 .update_lsp_status(server_name, status);
10957 }
10958
10959 async fn read_text_file(&self, path: PathBuf) -> Result<String> {
10960 if self.worktree.entry_for_path(&path).is_none() {
10961 return Err(anyhow!("no such path {path:?}"));
10962 }
10963 let path = self.worktree.absolutize(path.as_ref())?;
10964 let content = self.fs.load(&path).await?;
10965 Ok(content)
10966 }
10967}
10968
10969fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
10970 proto::Symbol {
10971 language_server_name: symbol.language_server_name.0.to_string(),
10972 source_worktree_id: symbol.source_worktree_id.to_proto(),
10973 worktree_id: symbol.path.worktree_id.to_proto(),
10974 path: symbol.path.path.to_string_lossy().to_string(),
10975 name: symbol.name.clone(),
10976 kind: unsafe { mem::transmute::<lsp::SymbolKind, i32>(symbol.kind) },
10977 start: Some(proto::PointUtf16 {
10978 row: symbol.range.start.0.row,
10979 column: symbol.range.start.0.column,
10980 }),
10981 end: Some(proto::PointUtf16 {
10982 row: symbol.range.end.0.row,
10983 column: symbol.range.end.0.column,
10984 }),
10985 signature: symbol.signature.to_vec(),
10986 }
10987}
10988
10989fn relativize_path(base: &Path, path: &Path) -> PathBuf {
10990 let mut path_components = path.components();
10991 let mut base_components = base.components();
10992 let mut components: Vec<Component> = Vec::new();
10993 loop {
10994 match (path_components.next(), base_components.next()) {
10995 (None, None) => break,
10996 (Some(a), None) => {
10997 components.push(a);
10998 components.extend(path_components.by_ref());
10999 break;
11000 }
11001 (None, _) => components.push(Component::ParentDir),
11002 (Some(a), Some(b)) if components.is_empty() && a == b => (),
11003 (Some(a), Some(Component::CurDir)) => components.push(a),
11004 (Some(a), Some(_)) => {
11005 components.push(Component::ParentDir);
11006 for _ in base_components {
11007 components.push(Component::ParentDir);
11008 }
11009 components.push(a);
11010 components.extend(path_components.by_ref());
11011 break;
11012 }
11013 }
11014 }
11015 components.iter().map(|c| c.as_os_str()).collect()
11016}
11017
11018fn resolve_path(base: &Path, path: &Path) -> PathBuf {
11019 let mut result = base.to_path_buf();
11020 for component in path.components() {
11021 match component {
11022 Component::ParentDir => {
11023 result.pop();
11024 }
11025 Component::CurDir => (),
11026 _ => result.push(component),
11027 }
11028 }
11029 result
11030}
11031
11032/// ResolvedPath is a path that has been resolved to either a ProjectPath
11033/// or an AbsPath and that *exists*.
11034#[derive(Debug, Clone)]
11035pub enum ResolvedPath {
11036 ProjectPath(ProjectPath),
11037 AbsPath(PathBuf),
11038}
11039
11040impl Item for Buffer {
11041 fn try_open(
11042 project: &Model<Project>,
11043 path: &ProjectPath,
11044 cx: &mut AppContext,
11045 ) -> Option<Task<Result<Model<Self>>>> {
11046 Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
11047 }
11048
11049 fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
11050 File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
11051 }
11052
11053 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
11054 File::from_dyn(self.file()).map(|file| ProjectPath {
11055 worktree_id: file.worktree_id(cx),
11056 path: file.path().clone(),
11057 })
11058 }
11059}
11060
11061impl Completion {
11062 /// A key that can be used to sort completions when displaying
11063 /// them to the user.
11064 pub fn sort_key(&self) -> (usize, &str) {
11065 let kind_key = match self.lsp_completion.kind {
11066 Some(lsp::CompletionItemKind::KEYWORD) => 0,
11067 Some(lsp::CompletionItemKind::VARIABLE) => 1,
11068 _ => 2,
11069 };
11070 (kind_key, &self.label.text[self.label.filter_range.clone()])
11071 }
11072
11073 /// Whether this completion is a snippet.
11074 pub fn is_snippet(&self) -> bool {
11075 self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
11076 }
11077}
11078
11079fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
11080 match server.capabilities().text_document_sync.as_ref()? {
11081 lsp::TextDocumentSyncCapability::Kind(kind) => match kind {
11082 &lsp::TextDocumentSyncKind::NONE => None,
11083 &lsp::TextDocumentSyncKind::FULL => Some(true),
11084 &lsp::TextDocumentSyncKind::INCREMENTAL => Some(false),
11085 _ => None,
11086 },
11087 lsp::TextDocumentSyncCapability::Options(options) => match options.save.as_ref()? {
11088 lsp::TextDocumentSyncSaveOptions::Supported(supported) => {
11089 if *supported {
11090 Some(true)
11091 } else {
11092 None
11093 }
11094 }
11095 lsp::TextDocumentSyncSaveOptions::SaveOptions(save_options) => {
11096 Some(save_options.include_text.unwrap_or(false))
11097 }
11098 },
11099 }
11100}
11101
11102async fn load_direnv_environment(dir: &Path) -> Result<Option<HashMap<String, String>>> {
11103 let Ok(direnv_path) = which::which("direnv") else {
11104 return Ok(None);
11105 };
11106
11107 let direnv_output = smol::process::Command::new(direnv_path)
11108 .args(["export", "json"])
11109 .current_dir(dir)
11110 .output()
11111 .await
11112 .context("failed to spawn direnv to get local environment variables")?;
11113
11114 anyhow::ensure!(
11115 direnv_output.status.success(),
11116 "direnv exited with error {:?}",
11117 direnv_output.status
11118 );
11119
11120 let output = String::from_utf8_lossy(&direnv_output.stdout);
11121 if output.is_empty() {
11122 return Ok(None);
11123 }
11124
11125 Ok(Some(
11126 serde_json::from_str(&output).context("failed to parse direnv output")?,
11127 ))
11128}
11129
11130async fn load_shell_environment(
11131 dir: &Path,
11132 load_direnv: &DirenvSettings,
11133) -> Result<HashMap<String, String>> {
11134 let direnv_environment = match load_direnv {
11135 DirenvSettings::ShellHook => None,
11136 DirenvSettings::Direct => load_direnv_environment(dir).await?,
11137 }
11138 .unwrap_or(HashMap::default());
11139
11140 let marker = "ZED_SHELL_START";
11141 let shell = env::var("SHELL").context(
11142 "SHELL environment variable is not assigned so we can't source login environment variables",
11143 )?;
11144
11145 // What we're doing here is to spawn a shell and then `cd` into
11146 // the project directory to get the env in there as if the user
11147 // `cd`'d into it. We do that because tools like direnv, asdf, ...
11148 // hook into `cd` and only set up the env after that.
11149 //
11150 // If the user selects `Direct` for direnv, it would set an environment
11151 // variable that later uses to know that it should not run the hook.
11152 // We would include in `.envs` call so it is okay to run the hook
11153 // even if direnv direct mode is enabled.
11154 //
11155 // In certain shells we need to execute additional_command in order to
11156 // trigger the behavior of direnv, etc.
11157 //
11158 //
11159 // The `exit 0` is the result of hours of debugging, trying to find out
11160 // why running this command here, without `exit 0`, would mess
11161 // up signal process for our process so that `ctrl-c` doesn't work
11162 // anymore.
11163 //
11164 // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
11165 // do that, but it does, and `exit 0` helps.
11166 let additional_command = PathBuf::from(&shell)
11167 .file_name()
11168 .and_then(|f| f.to_str())
11169 .and_then(|shell| match shell {
11170 "fish" => Some("emit fish_prompt;"),
11171 _ => None,
11172 });
11173
11174 let command = format!(
11175 "cd '{}';{} printf '%s' {marker}; /usr/bin/env; exit 0;",
11176 dir.display(),
11177 additional_command.unwrap_or("")
11178 );
11179
11180 let output = smol::process::Command::new(&shell)
11181 .args(["-i", "-c", &command])
11182 .envs(direnv_environment)
11183 .output()
11184 .await
11185 .context("failed to spawn login shell to source login environment variables")?;
11186
11187 anyhow::ensure!(
11188 output.status.success(),
11189 "login shell exited with error {:?}",
11190 output.status
11191 );
11192
11193 let stdout = String::from_utf8_lossy(&output.stdout);
11194 let env_output_start = stdout.find(marker).ok_or_else(|| {
11195 anyhow!(
11196 "failed to parse output of `env` command in login shell: {}",
11197 stdout
11198 )
11199 })?;
11200
11201 let mut parsed_env = HashMap::default();
11202 let env_output = &stdout[env_output_start + marker.len()..];
11203
11204 parse_env_output(env_output, |key, value| {
11205 parsed_env.insert(key, value);
11206 });
11207
11208 Ok(parsed_env)
11209}
11210
11211fn remove_empty_hover_blocks(mut hover: Hover) -> Option<Hover> {
11212 hover
11213 .contents
11214 .retain(|hover_block| !hover_block.text.trim().is_empty());
11215 if hover.contents.is_empty() {
11216 None
11217 } else {
11218 Some(hover)
11219 }
11220}
11221
11222#[derive(Debug)]
11223pub struct NoRepositoryError {}
11224
11225impl std::fmt::Display for NoRepositoryError {
11226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11227 write!(f, "no git repository for worktree found")
11228 }
11229}
11230
11231impl std::error::Error for NoRepositoryError {}
11232
11233fn serialize_location(location: &Location, cx: &AppContext) -> proto::Location {
11234 proto::Location {
11235 buffer_id: location.buffer.read(cx).remote_id().into(),
11236 start: Some(serialize_anchor(&location.range.start)),
11237 end: Some(serialize_anchor(&location.range.end)),
11238 }
11239}
11240
11241fn deserialize_location(
11242 project: &Model<Project>,
11243 location: proto::Location,
11244 cx: &mut AppContext,
11245) -> Task<Result<Location>> {
11246 let buffer_id = match BufferId::new(location.buffer_id) {
11247 Ok(id) => id,
11248 Err(e) => return Task::ready(Err(e)),
11249 };
11250 let buffer_task = project.update(cx, |project, cx| {
11251 project.wait_for_remote_buffer(buffer_id, cx)
11252 });
11253 cx.spawn(|_| async move {
11254 let buffer = buffer_task.await?;
11255 let start = location
11256 .start
11257 .and_then(deserialize_anchor)
11258 .context("missing task context location start")?;
11259 let end = location
11260 .end
11261 .and_then(deserialize_anchor)
11262 .context("missing task context location end")?;
11263 Ok(Location {
11264 buffer,
11265 range: start..end,
11266 })
11267 })
11268}
11269
11270#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
11271pub struct DiagnosticSummary {
11272 pub error_count: usize,
11273 pub warning_count: usize,
11274}
11275
11276impl DiagnosticSummary {
11277 pub fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
11278 let mut this = Self {
11279 error_count: 0,
11280 warning_count: 0,
11281 };
11282
11283 for entry in diagnostics {
11284 if entry.diagnostic.is_primary {
11285 match entry.diagnostic.severity {
11286 DiagnosticSeverity::ERROR => this.error_count += 1,
11287 DiagnosticSeverity::WARNING => this.warning_count += 1,
11288 _ => {}
11289 }
11290 }
11291 }
11292
11293 this
11294 }
11295
11296 pub fn is_empty(&self) -> bool {
11297 self.error_count == 0 && self.warning_count == 0
11298 }
11299
11300 pub fn to_proto(
11301 &self,
11302 language_server_id: LanguageServerId,
11303 path: &Path,
11304 ) -> proto::DiagnosticSummary {
11305 proto::DiagnosticSummary {
11306 path: path.to_string_lossy().to_string(),
11307 language_server_id: language_server_id.0 as u64,
11308 error_count: self.error_count as u32,
11309 warning_count: self.warning_count as u32,
11310 }
11311 }
11312}
11313
11314pub fn sort_worktree_entries(entries: &mut Vec<Entry>) {
11315 entries.sort_by(|entry_a, entry_b| {
11316 compare_paths(
11317 (&entry_a.path, entry_a.is_file()),
11318 (&entry_b.path, entry_b.is_file()),
11319 )
11320 });
11321}