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