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