project.rs

   1pub mod fs;
   2mod ignore;
   3mod lsp_command;
   4pub mod search;
   5pub mod worktree;
   6
   7use anyhow::{anyhow, Context, Result};
   8use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
   9use clock::ReplicaId;
  10use collections::{hash_map, BTreeMap, HashMap, HashSet};
  11use futures::{future::Shared, Future, FutureExt, StreamExt, TryFutureExt};
  12use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
  13use gpui::{
  14    AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
  15    MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle,
  16};
  17use language::{
  18    point_to_lsp,
  19    proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
  20    range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CodeAction, CodeLabel, Completion,
  21    Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language,
  22    LanguageRegistry, LanguageServerName, LocalFile, LspAdapter, OffsetRangeExt, Operation, Patch,
  23    PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction,
  24};
  25use lsp::{DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer};
  26use lsp_command::*;
  27use parking_lot::Mutex;
  28use postage::watch;
  29use rand::prelude::*;
  30use search::SearchQuery;
  31use serde::Serialize;
  32use settings::Settings;
  33use sha2::{Digest, Sha256};
  34use similar::{ChangeTag, TextDiff};
  35use std::{
  36    cell::RefCell,
  37    cmp::{self, Ordering},
  38    convert::TryInto,
  39    hash::Hash,
  40    mem,
  41    ops::Range,
  42    path::{Component, Path, PathBuf},
  43    rc::Rc,
  44    sync::{
  45        atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
  46        Arc,
  47    },
  48    time::Instant,
  49};
  50use util::{post_inc, ResultExt, TryFutureExt as _};
  51
  52pub use fs::*;
  53pub use worktree::*;
  54
  55pub trait Item: Entity {
  56    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
  57}
  58
  59pub struct Project {
  60    worktrees: Vec<WorktreeHandle>,
  61    active_entry: Option<ProjectEntryId>,
  62    languages: Arc<LanguageRegistry>,
  63    language_servers:
  64        HashMap<(WorktreeId, LanguageServerName), (Arc<dyn LspAdapter>, Arc<LanguageServer>)>,
  65    started_language_servers:
  66        HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
  67    language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
  68    language_server_settings: Arc<Mutex<serde_json::Value>>,
  69    last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
  70    next_language_server_id: usize,
  71    client: Arc<client::Client>,
  72    next_entry_id: Arc<AtomicUsize>,
  73    user_store: ModelHandle<UserStore>,
  74    fs: Arc<dyn Fs>,
  75    client_state: ProjectClientState,
  76    collaborators: HashMap<PeerId, Collaborator>,
  77    subscriptions: Vec<client::Subscription>,
  78    opened_buffer: (Rc<RefCell<watch::Sender<()>>>, watch::Receiver<()>),
  79    shared_buffers: HashMap<PeerId, HashSet<u64>>,
  80    loading_buffers: HashMap<
  81        ProjectPath,
  82        postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
  83    >,
  84    loading_local_worktrees:
  85        HashMap<Arc<Path>, Shared<Task<Result<ModelHandle<Worktree>, Arc<anyhow::Error>>>>>,
  86    opened_buffers: HashMap<u64, OpenBuffer>,
  87    buffer_snapshots: HashMap<u64, Vec<(i32, TextBufferSnapshot)>>,
  88    nonce: u128,
  89}
  90
  91enum OpenBuffer {
  92    Strong(ModelHandle<Buffer>),
  93    Weak(WeakModelHandle<Buffer>),
  94    Loading(Vec<Operation>),
  95}
  96
  97enum WorktreeHandle {
  98    Strong(ModelHandle<Worktree>),
  99    Weak(WeakModelHandle<Worktree>),
 100}
 101
 102enum ProjectClientState {
 103    Local {
 104        is_shared: bool,
 105        remote_id_tx: watch::Sender<Option<u64>>,
 106        remote_id_rx: watch::Receiver<Option<u64>>,
 107        _maintain_remote_id_task: Task<Option<()>>,
 108    },
 109    Remote {
 110        sharing_has_stopped: bool,
 111        remote_id: u64,
 112        replica_id: ReplicaId,
 113        _detect_unshare_task: Task<Option<()>>,
 114    },
 115}
 116
 117#[derive(Clone, Debug)]
 118pub struct Collaborator {
 119    pub user: Arc<User>,
 120    pub peer_id: PeerId,
 121    pub replica_id: ReplicaId,
 122}
 123
 124#[derive(Clone, Debug, PartialEq)]
 125pub enum Event {
 126    ActiveEntryChanged(Option<ProjectEntryId>),
 127    WorktreeRemoved(WorktreeId),
 128    DiskBasedDiagnosticsStarted,
 129    DiskBasedDiagnosticsUpdated,
 130    DiskBasedDiagnosticsFinished,
 131    DiagnosticsUpdated(ProjectPath),
 132    RemoteIdChanged(Option<u64>),
 133    CollaboratorLeft(PeerId),
 134}
 135
 136#[derive(Serialize)]
 137pub struct LanguageServerStatus {
 138    pub name: String,
 139    pub pending_work: BTreeMap<String, LanguageServerProgress>,
 140    pub pending_diagnostic_updates: isize,
 141}
 142
 143#[derive(Clone, Debug, Serialize)]
 144pub struct LanguageServerProgress {
 145    pub message: Option<String>,
 146    pub percentage: Option<usize>,
 147    #[serde(skip_serializing)]
 148    pub last_update_at: Instant,
 149}
 150
 151#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
 152pub struct ProjectPath {
 153    pub worktree_id: WorktreeId,
 154    pub path: Arc<Path>,
 155}
 156
 157#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
 158pub struct DiagnosticSummary {
 159    pub error_count: usize,
 160    pub warning_count: usize,
 161}
 162
 163#[derive(Debug)]
 164pub struct Location {
 165    pub buffer: ModelHandle<Buffer>,
 166    pub range: Range<language::Anchor>,
 167}
 168
 169#[derive(Debug)]
 170pub struct DocumentHighlight {
 171    pub range: Range<language::Anchor>,
 172    pub kind: DocumentHighlightKind,
 173}
 174
 175#[derive(Clone, Debug)]
 176pub struct Symbol {
 177    pub source_worktree_id: WorktreeId,
 178    pub worktree_id: WorktreeId,
 179    pub language_server_name: LanguageServerName,
 180    pub path: PathBuf,
 181    pub label: CodeLabel,
 182    pub name: String,
 183    pub kind: lsp::SymbolKind,
 184    pub range: Range<PointUtf16>,
 185    pub signature: [u8; 32],
 186}
 187
 188#[derive(Default)]
 189pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
 190
 191impl DiagnosticSummary {
 192    fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
 193        let mut this = Self {
 194            error_count: 0,
 195            warning_count: 0,
 196        };
 197
 198        for entry in diagnostics {
 199            if entry.diagnostic.is_primary {
 200                match entry.diagnostic.severity {
 201                    DiagnosticSeverity::ERROR => this.error_count += 1,
 202                    DiagnosticSeverity::WARNING => this.warning_count += 1,
 203                    _ => {}
 204                }
 205            }
 206        }
 207
 208        this
 209    }
 210
 211    pub fn is_empty(&self) -> bool {
 212        self.error_count == 0 && self.warning_count == 0
 213    }
 214
 215    pub fn to_proto(&self, path: &Path) -> proto::DiagnosticSummary {
 216        proto::DiagnosticSummary {
 217            path: path.to_string_lossy().to_string(),
 218            error_count: self.error_count as u32,
 219            warning_count: self.warning_count as u32,
 220        }
 221    }
 222}
 223
 224#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
 225pub struct ProjectEntryId(usize);
 226
 227impl ProjectEntryId {
 228    pub fn new(counter: &AtomicUsize) -> Self {
 229        Self(counter.fetch_add(1, SeqCst))
 230    }
 231
 232    pub fn from_proto(id: u64) -> Self {
 233        Self(id as usize)
 234    }
 235
 236    pub fn to_proto(&self) -> u64 {
 237        self.0 as u64
 238    }
 239
 240    pub fn to_usize(&self) -> usize {
 241        self.0
 242    }
 243}
 244
 245impl Project {
 246    pub fn init(client: &Arc<Client>) {
 247        client.add_model_message_handler(Self::handle_add_collaborator);
 248        client.add_model_message_handler(Self::handle_buffer_reloaded);
 249        client.add_model_message_handler(Self::handle_buffer_saved);
 250        client.add_model_message_handler(Self::handle_start_language_server);
 251        client.add_model_message_handler(Self::handle_update_language_server);
 252        client.add_model_message_handler(Self::handle_remove_collaborator);
 253        client.add_model_message_handler(Self::handle_register_worktree);
 254        client.add_model_message_handler(Self::handle_unregister_worktree);
 255        client.add_model_message_handler(Self::handle_unshare_project);
 256        client.add_model_message_handler(Self::handle_update_buffer_file);
 257        client.add_model_message_handler(Self::handle_update_buffer);
 258        client.add_model_message_handler(Self::handle_update_diagnostic_summary);
 259        client.add_model_message_handler(Self::handle_update_worktree);
 260        client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
 261        client.add_model_request_handler(Self::handle_apply_code_action);
 262        client.add_model_request_handler(Self::handle_reload_buffers);
 263        client.add_model_request_handler(Self::handle_format_buffers);
 264        client.add_model_request_handler(Self::handle_get_code_actions);
 265        client.add_model_request_handler(Self::handle_get_completions);
 266        client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
 267        client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
 268        client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
 269        client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
 270        client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
 271        client.add_model_request_handler(Self::handle_search_project);
 272        client.add_model_request_handler(Self::handle_get_project_symbols);
 273        client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
 274        client.add_model_request_handler(Self::handle_open_buffer_by_id);
 275        client.add_model_request_handler(Self::handle_open_buffer_by_path);
 276        client.add_model_request_handler(Self::handle_save_buffer);
 277    }
 278
 279    pub fn local(
 280        client: Arc<Client>,
 281        user_store: ModelHandle<UserStore>,
 282        languages: Arc<LanguageRegistry>,
 283        fs: Arc<dyn Fs>,
 284        cx: &mut MutableAppContext,
 285    ) -> ModelHandle<Self> {
 286        cx.add_model(|cx: &mut ModelContext<Self>| {
 287            let (remote_id_tx, remote_id_rx) = watch::channel();
 288            let _maintain_remote_id_task = cx.spawn_weak({
 289                let rpc = client.clone();
 290                move |this, mut cx| {
 291                    async move {
 292                        let mut status = rpc.status();
 293                        while let Some(status) = status.next().await {
 294                            if let Some(this) = this.upgrade(&cx) {
 295                                if status.is_connected() {
 296                                    this.update(&mut cx, |this, cx| this.register(cx)).await?;
 297                                } else {
 298                                    this.update(&mut cx, |this, cx| this.unregister(cx));
 299                                }
 300                            }
 301                        }
 302                        Ok(())
 303                    }
 304                    .log_err()
 305                }
 306            });
 307
 308            let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
 309            Self {
 310                worktrees: Default::default(),
 311                collaborators: Default::default(),
 312                opened_buffers: Default::default(),
 313                shared_buffers: Default::default(),
 314                loading_buffers: Default::default(),
 315                loading_local_worktrees: Default::default(),
 316                buffer_snapshots: Default::default(),
 317                client_state: ProjectClientState::Local {
 318                    is_shared: false,
 319                    remote_id_tx,
 320                    remote_id_rx,
 321                    _maintain_remote_id_task,
 322                },
 323                opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
 324                subscriptions: Vec::new(),
 325                active_entry: None,
 326                languages,
 327                client,
 328                user_store,
 329                fs,
 330                next_entry_id: Default::default(),
 331                language_servers: Default::default(),
 332                started_language_servers: Default::default(),
 333                language_server_statuses: Default::default(),
 334                last_workspace_edits_by_language_server: Default::default(),
 335                language_server_settings: Default::default(),
 336                next_language_server_id: 0,
 337                nonce: StdRng::from_entropy().gen(),
 338            }
 339        })
 340    }
 341
 342    pub async fn remote(
 343        remote_id: u64,
 344        client: Arc<Client>,
 345        user_store: ModelHandle<UserStore>,
 346        languages: Arc<LanguageRegistry>,
 347        fs: Arc<dyn Fs>,
 348        cx: &mut AsyncAppContext,
 349    ) -> Result<ModelHandle<Self>> {
 350        client.authenticate_and_connect(true, &cx).await?;
 351
 352        let response = client
 353            .request(proto::JoinProject {
 354                project_id: remote_id,
 355            })
 356            .await?;
 357
 358        let replica_id = response.replica_id as ReplicaId;
 359
 360        let mut worktrees = Vec::new();
 361        for worktree in response.worktrees {
 362            let (worktree, load_task) = cx
 363                .update(|cx| Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx));
 364            worktrees.push(worktree);
 365            load_task.detach();
 366        }
 367
 368        let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
 369        let this = cx.add_model(|cx: &mut ModelContext<Self>| {
 370            let mut this = Self {
 371                worktrees: Vec::new(),
 372                loading_buffers: Default::default(),
 373                opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
 374                shared_buffers: Default::default(),
 375                loading_local_worktrees: Default::default(),
 376                active_entry: None,
 377                collaborators: Default::default(),
 378                languages,
 379                user_store: user_store.clone(),
 380                fs,
 381                next_entry_id: Default::default(),
 382                subscriptions: vec![client.add_model_for_remote_entity(remote_id, cx)],
 383                client: client.clone(),
 384                client_state: ProjectClientState::Remote {
 385                    sharing_has_stopped: false,
 386                    remote_id,
 387                    replica_id,
 388                    _detect_unshare_task: cx.spawn_weak(move |this, mut cx| {
 389                        async move {
 390                            let mut status = client.status();
 391                            let is_connected =
 392                                status.next().await.map_or(false, |s| s.is_connected());
 393                            // Even if we're initially connected, any future change of the status means we momentarily disconnected.
 394                            if !is_connected || status.next().await.is_some() {
 395                                if let Some(this) = this.upgrade(&cx) {
 396                                    this.update(&mut cx, |this, cx| this.project_unshared(cx))
 397                                }
 398                            }
 399                            Ok(())
 400                        }
 401                        .log_err()
 402                    }),
 403                },
 404                language_servers: Default::default(),
 405                started_language_servers: Default::default(),
 406                language_server_settings: Default::default(),
 407                language_server_statuses: response
 408                    .language_servers
 409                    .into_iter()
 410                    .map(|server| {
 411                        (
 412                            server.id as usize,
 413                            LanguageServerStatus {
 414                                name: server.name,
 415                                pending_work: Default::default(),
 416                                pending_diagnostic_updates: 0,
 417                            },
 418                        )
 419                    })
 420                    .collect(),
 421                last_workspace_edits_by_language_server: Default::default(),
 422                next_language_server_id: 0,
 423                opened_buffers: Default::default(),
 424                buffer_snapshots: Default::default(),
 425                nonce: StdRng::from_entropy().gen(),
 426            };
 427            for worktree in worktrees {
 428                this.add_worktree(&worktree, cx);
 429            }
 430            this
 431        });
 432
 433        let user_ids = response
 434            .collaborators
 435            .iter()
 436            .map(|peer| peer.user_id)
 437            .collect();
 438        user_store
 439            .update(cx, |user_store, cx| user_store.load_users(user_ids, cx))
 440            .await?;
 441        let mut collaborators = HashMap::default();
 442        for message in response.collaborators {
 443            let collaborator = Collaborator::from_proto(message, &user_store, cx).await?;
 444            collaborators.insert(collaborator.peer_id, collaborator);
 445        }
 446
 447        this.update(cx, |this, _| {
 448            this.collaborators = collaborators;
 449        });
 450
 451        Ok(this)
 452    }
 453
 454    #[cfg(any(test, feature = "test-support"))]
 455    pub fn test(fs: Arc<dyn Fs>, cx: &mut gpui::TestAppContext) -> ModelHandle<Project> {
 456        let languages = Arc::new(LanguageRegistry::test());
 457        let http_client = client::test::FakeHttpClient::with_404_response();
 458        let client = client::Client::new(http_client.clone());
 459        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
 460        cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
 461    }
 462
 463    pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
 464        self.opened_buffers
 465            .get(&remote_id)
 466            .and_then(|buffer| buffer.upgrade(cx))
 467    }
 468
 469    pub fn languages(&self) -> &Arc<LanguageRegistry> {
 470        &self.languages
 471    }
 472
 473    #[cfg(any(test, feature = "test-support"))]
 474    pub fn check_invariants(&self, cx: &AppContext) {
 475        if self.is_local() {
 476            let mut worktree_root_paths = HashMap::default();
 477            for worktree in self.worktrees(cx) {
 478                let worktree = worktree.read(cx);
 479                let abs_path = worktree.as_local().unwrap().abs_path().clone();
 480                let prev_worktree_id = worktree_root_paths.insert(abs_path.clone(), worktree.id());
 481                assert_eq!(
 482                    prev_worktree_id,
 483                    None,
 484                    "abs path {:?} for worktree {:?} is not unique ({:?} was already registered with the same path)",
 485                    abs_path,
 486                    worktree.id(),
 487                    prev_worktree_id
 488                )
 489            }
 490        } else {
 491            let replica_id = self.replica_id();
 492            for buffer in self.opened_buffers.values() {
 493                if let Some(buffer) = buffer.upgrade(cx) {
 494                    let buffer = buffer.read(cx);
 495                    assert_eq!(
 496                        buffer.deferred_ops_len(),
 497                        0,
 498                        "replica {}, buffer {} has deferred operations",
 499                        replica_id,
 500                        buffer.remote_id()
 501                    );
 502                }
 503            }
 504        }
 505    }
 506
 507    #[cfg(any(test, feature = "test-support"))]
 508    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
 509        let path = path.into();
 510        if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
 511            self.opened_buffers.iter().any(|(_, buffer)| {
 512                if let Some(buffer) = buffer.upgrade(cx) {
 513                    if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
 514                        if file.worktree == worktree && file.path() == &path.path {
 515                            return true;
 516                        }
 517                    }
 518                }
 519                false
 520            })
 521        } else {
 522            false
 523        }
 524    }
 525
 526    pub fn fs(&self) -> &Arc<dyn Fs> {
 527        &self.fs
 528    }
 529
 530    fn unregister(&mut self, cx: &mut ModelContext<Self>) {
 531        self.unshare(cx);
 532        for worktree in &self.worktrees {
 533            if let Some(worktree) = worktree.upgrade(cx) {
 534                worktree.update(cx, |worktree, _| {
 535                    worktree.as_local_mut().unwrap().unregister();
 536                });
 537            }
 538        }
 539
 540        if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
 541            *remote_id_tx.borrow_mut() = None;
 542        }
 543
 544        self.subscriptions.clear();
 545    }
 546
 547    fn register(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
 548        self.unregister(cx);
 549
 550        let response = self.client.request(proto::RegisterProject {});
 551        cx.spawn(|this, mut cx| async move {
 552            let remote_id = response.await?.project_id;
 553
 554            let mut registrations = Vec::new();
 555            this.update(&mut cx, |this, cx| {
 556                if let ProjectClientState::Local { remote_id_tx, .. } = &mut this.client_state {
 557                    *remote_id_tx.borrow_mut() = Some(remote_id);
 558                }
 559
 560                cx.emit(Event::RemoteIdChanged(Some(remote_id)));
 561
 562                this.subscriptions
 563                    .push(this.client.add_model_for_remote_entity(remote_id, cx));
 564
 565                for worktree in &this.worktrees {
 566                    if let Some(worktree) = worktree.upgrade(cx) {
 567                        registrations.push(worktree.update(cx, |worktree, cx| {
 568                            let worktree = worktree.as_local_mut().unwrap();
 569                            worktree.register(remote_id, cx)
 570                        }));
 571                    }
 572                }
 573            });
 574
 575            futures::future::try_join_all(registrations).await?;
 576            Ok(())
 577        })
 578    }
 579
 580    pub fn remote_id(&self) -> Option<u64> {
 581        match &self.client_state {
 582            ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
 583            ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
 584        }
 585    }
 586
 587    pub fn next_remote_id(&self) -> impl Future<Output = u64> {
 588        let mut id = None;
 589        let mut watch = None;
 590        match &self.client_state {
 591            ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
 592            ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
 593        }
 594
 595        async move {
 596            if let Some(id) = id {
 597                return id;
 598            }
 599            let mut watch = watch.unwrap();
 600            loop {
 601                let id = *watch.borrow();
 602                if let Some(id) = id {
 603                    return id;
 604                }
 605                watch.next().await;
 606            }
 607        }
 608    }
 609
 610    pub fn replica_id(&self) -> ReplicaId {
 611        match &self.client_state {
 612            ProjectClientState::Local { .. } => 0,
 613            ProjectClientState::Remote { replica_id, .. } => *replica_id,
 614        }
 615    }
 616
 617    pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
 618        &self.collaborators
 619    }
 620
 621    pub fn worktrees<'a>(
 622        &'a self,
 623        cx: &'a AppContext,
 624    ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
 625        self.worktrees
 626            .iter()
 627            .filter_map(move |worktree| worktree.upgrade(cx))
 628    }
 629
 630    pub fn visible_worktrees<'a>(
 631        &'a self,
 632        cx: &'a AppContext,
 633    ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
 634        self.worktrees.iter().filter_map(|worktree| {
 635            worktree.upgrade(cx).and_then(|worktree| {
 636                if worktree.read(cx).is_visible() {
 637                    Some(worktree)
 638                } else {
 639                    None
 640                }
 641            })
 642        })
 643    }
 644
 645    pub fn worktree_for_id(
 646        &self,
 647        id: WorktreeId,
 648        cx: &AppContext,
 649    ) -> Option<ModelHandle<Worktree>> {
 650        self.worktrees(cx)
 651            .find(|worktree| worktree.read(cx).id() == id)
 652    }
 653
 654    pub fn worktree_for_entry(
 655        &self,
 656        entry_id: ProjectEntryId,
 657        cx: &AppContext,
 658    ) -> Option<ModelHandle<Worktree>> {
 659        self.worktrees(cx)
 660            .find(|worktree| worktree.read(cx).contains_entry(entry_id))
 661    }
 662
 663    pub fn worktree_id_for_entry(
 664        &self,
 665        entry_id: ProjectEntryId,
 666        cx: &AppContext,
 667    ) -> Option<WorktreeId> {
 668        self.worktree_for_entry(entry_id, cx)
 669            .map(|worktree| worktree.read(cx).id())
 670    }
 671
 672    pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
 673        let rpc = self.client.clone();
 674        cx.spawn(|this, mut cx| async move {
 675            let project_id = this.update(&mut cx, |this, cx| {
 676                if let ProjectClientState::Local {
 677                    is_shared,
 678                    remote_id_rx,
 679                    ..
 680                } = &mut this.client_state
 681                {
 682                    *is_shared = true;
 683
 684                    for open_buffer in this.opened_buffers.values_mut() {
 685                        match open_buffer {
 686                            OpenBuffer::Strong(_) => {}
 687                            OpenBuffer::Weak(buffer) => {
 688                                if let Some(buffer) = buffer.upgrade(cx) {
 689                                    *open_buffer = OpenBuffer::Strong(buffer);
 690                                }
 691                            }
 692                            OpenBuffer::Loading(_) => unreachable!(),
 693                        }
 694                    }
 695
 696                    for worktree_handle in this.worktrees.iter_mut() {
 697                        match worktree_handle {
 698                            WorktreeHandle::Strong(_) => {}
 699                            WorktreeHandle::Weak(worktree) => {
 700                                if let Some(worktree) = worktree.upgrade(cx) {
 701                                    *worktree_handle = WorktreeHandle::Strong(worktree);
 702                                }
 703                            }
 704                        }
 705                    }
 706
 707                    remote_id_rx
 708                        .borrow()
 709                        .ok_or_else(|| anyhow!("no project id"))
 710                } else {
 711                    Err(anyhow!("can't share a remote project"))
 712                }
 713            })?;
 714
 715            rpc.request(proto::ShareProject { project_id }).await?;
 716
 717            let mut tasks = Vec::new();
 718            this.update(&mut cx, |this, cx| {
 719                for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 720                    worktree.update(cx, |worktree, cx| {
 721                        let worktree = worktree.as_local_mut().unwrap();
 722                        tasks.push(worktree.share(project_id, cx));
 723                    });
 724                }
 725            });
 726            for task in tasks {
 727                task.await?;
 728            }
 729            this.update(&mut cx, |_, cx| cx.notify());
 730            Ok(())
 731        })
 732    }
 733
 734    pub fn unshare(&mut self, cx: &mut ModelContext<Self>) {
 735        let rpc = self.client.clone();
 736
 737        if let ProjectClientState::Local {
 738            is_shared,
 739            remote_id_rx,
 740            ..
 741        } = &mut self.client_state
 742        {
 743            if !*is_shared {
 744                return;
 745            }
 746
 747            *is_shared = false;
 748            self.collaborators.clear();
 749            self.shared_buffers.clear();
 750            for worktree_handle in self.worktrees.iter_mut() {
 751                if let WorktreeHandle::Strong(worktree) = worktree_handle {
 752                    let is_visible = worktree.update(cx, |worktree, _| {
 753                        worktree.as_local_mut().unwrap().unshare();
 754                        worktree.is_visible()
 755                    });
 756                    if !is_visible {
 757                        *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
 758                    }
 759                }
 760            }
 761
 762            for open_buffer in self.opened_buffers.values_mut() {
 763                match open_buffer {
 764                    OpenBuffer::Strong(buffer) => {
 765                        *open_buffer = OpenBuffer::Weak(buffer.downgrade());
 766                    }
 767                    _ => {}
 768                }
 769            }
 770
 771            if let Some(project_id) = *remote_id_rx.borrow() {
 772                rpc.send(proto::UnshareProject { project_id }).log_err();
 773            }
 774
 775            cx.notify();
 776        } else {
 777            log::error!("attempted to unshare a remote project");
 778        }
 779    }
 780
 781    fn project_unshared(&mut self, cx: &mut ModelContext<Self>) {
 782        if let ProjectClientState::Remote {
 783            sharing_has_stopped,
 784            ..
 785        } = &mut self.client_state
 786        {
 787            *sharing_has_stopped = true;
 788            self.collaborators.clear();
 789            cx.notify();
 790        }
 791    }
 792
 793    pub fn is_read_only(&self) -> bool {
 794        match &self.client_state {
 795            ProjectClientState::Local { .. } => false,
 796            ProjectClientState::Remote {
 797                sharing_has_stopped,
 798                ..
 799            } => *sharing_has_stopped,
 800        }
 801    }
 802
 803    pub fn is_local(&self) -> bool {
 804        match &self.client_state {
 805            ProjectClientState::Local { .. } => true,
 806            ProjectClientState::Remote { .. } => false,
 807        }
 808    }
 809
 810    pub fn is_remote(&self) -> bool {
 811        !self.is_local()
 812    }
 813
 814    pub fn create_buffer(
 815        &mut self,
 816        text: &str,
 817        language: Option<Arc<Language>>,
 818        cx: &mut ModelContext<Self>,
 819    ) -> Result<ModelHandle<Buffer>> {
 820        if self.is_remote() {
 821            return Err(anyhow!("creating buffers as a guest is not supported yet"));
 822        }
 823
 824        let buffer = cx.add_model(|cx| {
 825            Buffer::new(self.replica_id(), text, cx)
 826                .with_language(language.unwrap_or(language::PLAIN_TEXT.clone()), cx)
 827        });
 828        self.register_buffer(&buffer, cx)?;
 829        Ok(buffer)
 830    }
 831
 832    pub fn open_path(
 833        &mut self,
 834        path: impl Into<ProjectPath>,
 835        cx: &mut ModelContext<Self>,
 836    ) -> Task<Result<(ProjectEntryId, AnyModelHandle)>> {
 837        let task = self.open_buffer(path, cx);
 838        cx.spawn_weak(|_, cx| async move {
 839            let buffer = task.await?;
 840            let project_entry_id = buffer
 841                .read_with(&cx, |buffer, cx| {
 842                    File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
 843                })
 844                .ok_or_else(|| anyhow!("no project entry"))?;
 845            Ok((project_entry_id, buffer.into()))
 846        })
 847    }
 848
 849    pub fn open_buffer(
 850        &mut self,
 851        path: impl Into<ProjectPath>,
 852        cx: &mut ModelContext<Self>,
 853    ) -> Task<Result<ModelHandle<Buffer>>> {
 854        let project_path = path.into();
 855        let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
 856            worktree
 857        } else {
 858            return Task::ready(Err(anyhow!("no such worktree")));
 859        };
 860
 861        // If there is already a buffer for the given path, then return it.
 862        let existing_buffer = self.get_open_buffer(&project_path, cx);
 863        if let Some(existing_buffer) = existing_buffer {
 864            return Task::ready(Ok(existing_buffer));
 865        }
 866
 867        let mut loading_watch = match self.loading_buffers.entry(project_path.clone()) {
 868            // If the given path is already being loaded, then wait for that existing
 869            // task to complete and return the same buffer.
 870            hash_map::Entry::Occupied(e) => e.get().clone(),
 871
 872            // Otherwise, record the fact that this path is now being loaded.
 873            hash_map::Entry::Vacant(entry) => {
 874                let (mut tx, rx) = postage::watch::channel();
 875                entry.insert(rx.clone());
 876
 877                let load_buffer = if worktree.read(cx).is_local() {
 878                    self.open_local_buffer(&project_path.path, &worktree, cx)
 879                } else {
 880                    self.open_remote_buffer(&project_path.path, &worktree, cx)
 881                };
 882
 883                cx.spawn(move |this, mut cx| async move {
 884                    let load_result = load_buffer.await;
 885                    *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
 886                        // Record the fact that the buffer is no longer loading.
 887                        this.loading_buffers.remove(&project_path);
 888                        let buffer = load_result.map_err(Arc::new)?;
 889                        Ok(buffer)
 890                    }));
 891                })
 892                .detach();
 893                rx
 894            }
 895        };
 896
 897        cx.foreground().spawn(async move {
 898            loop {
 899                if let Some(result) = loading_watch.borrow().as_ref() {
 900                    match result {
 901                        Ok(buffer) => return Ok(buffer.clone()),
 902                        Err(error) => return Err(anyhow!("{}", error)),
 903                    }
 904                }
 905                loading_watch.next().await;
 906            }
 907        })
 908    }
 909
 910    fn open_local_buffer(
 911        &mut self,
 912        path: &Arc<Path>,
 913        worktree: &ModelHandle<Worktree>,
 914        cx: &mut ModelContext<Self>,
 915    ) -> Task<Result<ModelHandle<Buffer>>> {
 916        let load_buffer = worktree.update(cx, |worktree, cx| {
 917            let worktree = worktree.as_local_mut().unwrap();
 918            worktree.load_buffer(path, cx)
 919        });
 920        cx.spawn(|this, mut cx| async move {
 921            let buffer = load_buffer.await?;
 922            this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
 923            Ok(buffer)
 924        })
 925    }
 926
 927    fn open_remote_buffer(
 928        &mut self,
 929        path: &Arc<Path>,
 930        worktree: &ModelHandle<Worktree>,
 931        cx: &mut ModelContext<Self>,
 932    ) -> Task<Result<ModelHandle<Buffer>>> {
 933        let rpc = self.client.clone();
 934        let project_id = self.remote_id().unwrap();
 935        let remote_worktree_id = worktree.read(cx).id();
 936        let path = path.clone();
 937        let path_string = path.to_string_lossy().to_string();
 938        cx.spawn(|this, mut cx| async move {
 939            let response = rpc
 940                .request(proto::OpenBufferByPath {
 941                    project_id,
 942                    worktree_id: remote_worktree_id.to_proto(),
 943                    path: path_string,
 944                })
 945                .await?;
 946            let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
 947            this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
 948                .await
 949        })
 950    }
 951
 952    fn open_local_buffer_via_lsp(
 953        &mut self,
 954        abs_path: lsp::Url,
 955        lsp_adapter: Arc<dyn LspAdapter>,
 956        lsp_server: Arc<LanguageServer>,
 957        cx: &mut ModelContext<Self>,
 958    ) -> Task<Result<ModelHandle<Buffer>>> {
 959        cx.spawn(|this, mut cx| async move {
 960            let abs_path = abs_path
 961                .to_file_path()
 962                .map_err(|_| anyhow!("can't convert URI to path"))?;
 963            let (worktree, relative_path) = if let Some(result) =
 964                this.read_with(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
 965            {
 966                result
 967            } else {
 968                let worktree = this
 969                    .update(&mut cx, |this, cx| {
 970                        this.create_local_worktree(&abs_path, false, cx)
 971                    })
 972                    .await?;
 973                this.update(&mut cx, |this, cx| {
 974                    this.language_servers.insert(
 975                        (worktree.read(cx).id(), lsp_adapter.name()),
 976                        (lsp_adapter, lsp_server),
 977                    );
 978                });
 979                (worktree, PathBuf::new())
 980            };
 981
 982            let project_path = ProjectPath {
 983                worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
 984                path: relative_path.into(),
 985            };
 986            this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
 987                .await
 988        })
 989    }
 990
 991    pub fn open_buffer_by_id(
 992        &mut self,
 993        id: u64,
 994        cx: &mut ModelContext<Self>,
 995    ) -> Task<Result<ModelHandle<Buffer>>> {
 996        if let Some(buffer) = self.buffer_for_id(id, cx) {
 997            Task::ready(Ok(buffer))
 998        } else if self.is_local() {
 999            Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1000        } else if let Some(project_id) = self.remote_id() {
1001            let request = self
1002                .client
1003                .request(proto::OpenBufferById { project_id, id });
1004            cx.spawn(|this, mut cx| async move {
1005                let buffer = request
1006                    .await?
1007                    .buffer
1008                    .ok_or_else(|| anyhow!("invalid buffer"))?;
1009                this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
1010                    .await
1011            })
1012        } else {
1013            Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1014        }
1015    }
1016
1017    pub fn save_buffer_as(
1018        &mut self,
1019        buffer: ModelHandle<Buffer>,
1020        abs_path: PathBuf,
1021        cx: &mut ModelContext<Project>,
1022    ) -> Task<Result<()>> {
1023        let worktree_task = self.find_or_create_local_worktree(&abs_path, true, cx);
1024        let old_path =
1025            File::from_dyn(buffer.read(cx).file()).and_then(|f| Some(f.as_local()?.abs_path(cx)));
1026        cx.spawn(|this, mut cx| async move {
1027            if let Some(old_path) = old_path {
1028                this.update(&mut cx, |this, cx| {
1029                    this.unregister_buffer_from_language_server(&buffer, old_path, cx);
1030                });
1031            }
1032            let (worktree, path) = worktree_task.await?;
1033            worktree
1034                .update(&mut cx, |worktree, cx| {
1035                    worktree
1036                        .as_local_mut()
1037                        .unwrap()
1038                        .save_buffer_as(buffer.clone(), path, cx)
1039                })
1040                .await?;
1041            this.update(&mut cx, |this, cx| {
1042                this.assign_language_to_buffer(&buffer, cx);
1043                this.register_buffer_with_language_server(&buffer, cx);
1044            });
1045            Ok(())
1046        })
1047    }
1048
1049    pub fn get_open_buffer(
1050        &mut self,
1051        path: &ProjectPath,
1052        cx: &mut ModelContext<Self>,
1053    ) -> Option<ModelHandle<Buffer>> {
1054        let worktree = self.worktree_for_id(path.worktree_id, cx)?;
1055        self.opened_buffers.values().find_map(|buffer| {
1056            let buffer = buffer.upgrade(cx)?;
1057            let file = File::from_dyn(buffer.read(cx).file())?;
1058            if file.worktree == worktree && file.path() == &path.path {
1059                Some(buffer)
1060            } else {
1061                None
1062            }
1063        })
1064    }
1065
1066    fn register_buffer(
1067        &mut self,
1068        buffer: &ModelHandle<Buffer>,
1069        cx: &mut ModelContext<Self>,
1070    ) -> Result<()> {
1071        let remote_id = buffer.read(cx).remote_id();
1072        let open_buffer = if self.is_remote() || self.is_shared() {
1073            OpenBuffer::Strong(buffer.clone())
1074        } else {
1075            OpenBuffer::Weak(buffer.downgrade())
1076        };
1077
1078        match self.opened_buffers.insert(remote_id, open_buffer) {
1079            None => {}
1080            Some(OpenBuffer::Loading(operations)) => {
1081                buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?
1082            }
1083            Some(OpenBuffer::Weak(existing_handle)) => {
1084                if existing_handle.upgrade(cx).is_some() {
1085                    Err(anyhow!(
1086                        "already registered buffer with remote id {}",
1087                        remote_id
1088                    ))?
1089                }
1090            }
1091            Some(OpenBuffer::Strong(_)) => Err(anyhow!(
1092                "already registered buffer with remote id {}",
1093                remote_id
1094            ))?,
1095        }
1096        cx.subscribe(buffer, |this, buffer, event, cx| {
1097            this.on_buffer_event(buffer, event, cx);
1098        })
1099        .detach();
1100
1101        self.assign_language_to_buffer(buffer, cx);
1102        self.register_buffer_with_language_server(buffer, cx);
1103        cx.observe_release(buffer, |this, buffer, cx| {
1104            if let Some(file) = File::from_dyn(buffer.file()) {
1105                if file.is_local() {
1106                    let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1107                    if let Some((_, server)) = this.language_server_for_buffer(buffer, cx) {
1108                        server
1109                            .notify::<lsp::notification::DidCloseTextDocument>(
1110                                lsp::DidCloseTextDocumentParams {
1111                                    text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
1112                                },
1113                            )
1114                            .log_err();
1115                    }
1116                }
1117            }
1118        })
1119        .detach();
1120
1121        Ok(())
1122    }
1123
1124    fn register_buffer_with_language_server(
1125        &mut self,
1126        buffer_handle: &ModelHandle<Buffer>,
1127        cx: &mut ModelContext<Self>,
1128    ) {
1129        let buffer = buffer_handle.read(cx);
1130        let buffer_id = buffer.remote_id();
1131        if let Some(file) = File::from_dyn(buffer.file()) {
1132            if file.is_local() {
1133                let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1134                let initial_snapshot = buffer.text_snapshot();
1135                let language_server = self.language_server_for_buffer(buffer, cx).cloned();
1136
1137                if let Some(local_worktree) = file.worktree.read(cx).as_local() {
1138                    if let Some(diagnostics) = local_worktree.diagnostics_for_path(file.path()) {
1139                        self.update_buffer_diagnostics(&buffer_handle, diagnostics, None, cx)
1140                            .log_err();
1141                    }
1142                }
1143
1144                if let Some((_, server)) = language_server {
1145                    server
1146                        .notify::<lsp::notification::DidOpenTextDocument>(
1147                            lsp::DidOpenTextDocumentParams {
1148                                text_document: lsp::TextDocumentItem::new(
1149                                    uri,
1150                                    Default::default(),
1151                                    0,
1152                                    initial_snapshot.text(),
1153                                ),
1154                            }
1155                            .clone(),
1156                        )
1157                        .log_err();
1158                    buffer_handle.update(cx, |buffer, cx| {
1159                        buffer.set_completion_triggers(
1160                            server
1161                                .capabilities()
1162                                .completion_provider
1163                                .as_ref()
1164                                .and_then(|provider| provider.trigger_characters.clone())
1165                                .unwrap_or(Vec::new()),
1166                            cx,
1167                        )
1168                    });
1169                    self.buffer_snapshots
1170                        .insert(buffer_id, vec![(0, initial_snapshot)]);
1171                }
1172            }
1173        }
1174    }
1175
1176    fn unregister_buffer_from_language_server(
1177        &mut self,
1178        buffer: &ModelHandle<Buffer>,
1179        old_path: PathBuf,
1180        cx: &mut ModelContext<Self>,
1181    ) {
1182        buffer.update(cx, |buffer, cx| {
1183            buffer.update_diagnostics(Default::default(), cx);
1184            self.buffer_snapshots.remove(&buffer.remote_id());
1185            if let Some((_, language_server)) = self.language_server_for_buffer(buffer, cx) {
1186                language_server
1187                    .notify::<lsp::notification::DidCloseTextDocument>(
1188                        lsp::DidCloseTextDocumentParams {
1189                            text_document: lsp::TextDocumentIdentifier::new(
1190                                lsp::Url::from_file_path(old_path).unwrap(),
1191                            ),
1192                        },
1193                    )
1194                    .log_err();
1195            }
1196        });
1197    }
1198
1199    fn on_buffer_event(
1200        &mut self,
1201        buffer: ModelHandle<Buffer>,
1202        event: &BufferEvent,
1203        cx: &mut ModelContext<Self>,
1204    ) -> Option<()> {
1205        match event {
1206            BufferEvent::Operation(operation) => {
1207                let project_id = self.remote_id()?;
1208                let request = self.client.request(proto::UpdateBuffer {
1209                    project_id,
1210                    buffer_id: buffer.read(cx).remote_id(),
1211                    operations: vec![language::proto::serialize_operation(&operation)],
1212                });
1213                cx.background().spawn(request).detach_and_log_err(cx);
1214            }
1215            BufferEvent::Edited { .. } => {
1216                let (_, language_server) = self
1217                    .language_server_for_buffer(buffer.read(cx), cx)?
1218                    .clone();
1219                let buffer = buffer.read(cx);
1220                let file = File::from_dyn(buffer.file())?;
1221                let abs_path = file.as_local()?.abs_path(cx);
1222                let uri = lsp::Url::from_file_path(abs_path).unwrap();
1223                let buffer_snapshots = self.buffer_snapshots.get_mut(&buffer.remote_id())?;
1224                let (version, prev_snapshot) = buffer_snapshots.last()?;
1225                let next_snapshot = buffer.text_snapshot();
1226                let next_version = version + 1;
1227
1228                let content_changes = buffer
1229                    .edits_since::<(PointUtf16, usize)>(prev_snapshot.version())
1230                    .map(|edit| {
1231                        let edit_start = edit.new.start.0;
1232                        let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
1233                        let new_text = next_snapshot
1234                            .text_for_range(edit.new.start.1..edit.new.end.1)
1235                            .collect();
1236                        lsp::TextDocumentContentChangeEvent {
1237                            range: Some(lsp::Range::new(
1238                                point_to_lsp(edit_start),
1239                                point_to_lsp(edit_end),
1240                            )),
1241                            range_length: None,
1242                            text: new_text,
1243                        }
1244                    })
1245                    .collect();
1246
1247                buffer_snapshots.push((next_version, next_snapshot));
1248
1249                language_server
1250                    .notify::<lsp::notification::DidChangeTextDocument>(
1251                        lsp::DidChangeTextDocumentParams {
1252                            text_document: lsp::VersionedTextDocumentIdentifier::new(
1253                                uri,
1254                                next_version,
1255                            ),
1256                            content_changes,
1257                        },
1258                    )
1259                    .log_err();
1260            }
1261            BufferEvent::Saved => {
1262                let file = File::from_dyn(buffer.read(cx).file())?;
1263                let worktree_id = file.worktree_id(cx);
1264                let abs_path = file.as_local()?.abs_path(cx);
1265                let text_document = lsp::TextDocumentIdentifier {
1266                    uri: lsp::Url::from_file_path(abs_path).unwrap(),
1267                };
1268
1269                for (_, server) in self.language_servers_for_worktree(worktree_id) {
1270                    server
1271                        .notify::<lsp::notification::DidSaveTextDocument>(
1272                            lsp::DidSaveTextDocumentParams {
1273                                text_document: text_document.clone(),
1274                                text: None,
1275                            },
1276                        )
1277                        .log_err();
1278                }
1279            }
1280            _ => {}
1281        }
1282
1283        None
1284    }
1285
1286    fn language_servers_for_worktree(
1287        &self,
1288        worktree_id: WorktreeId,
1289    ) -> impl Iterator<Item = &(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
1290        self.language_servers.iter().filter_map(
1291            move |((language_server_worktree_id, _), server)| {
1292                if *language_server_worktree_id == worktree_id {
1293                    Some(server)
1294                } else {
1295                    None
1296                }
1297            },
1298        )
1299    }
1300
1301    fn assign_language_to_buffer(
1302        &mut self,
1303        buffer: &ModelHandle<Buffer>,
1304        cx: &mut ModelContext<Self>,
1305    ) -> Option<()> {
1306        // If the buffer has a language, set it and start the language server if we haven't already.
1307        let full_path = buffer.read(cx).file()?.full_path(cx);
1308        let language = self.languages.select_language(&full_path)?;
1309        buffer.update(cx, |buffer, cx| {
1310            buffer.set_language(Some(language.clone()), cx);
1311        });
1312
1313        let file = File::from_dyn(buffer.read(cx).file())?;
1314        let worktree = file.worktree.read(cx).as_local()?;
1315        let worktree_id = worktree.id();
1316        let worktree_abs_path = worktree.abs_path().clone();
1317        self.start_language_server(worktree_id, worktree_abs_path, language, cx);
1318
1319        None
1320    }
1321
1322    fn start_language_server(
1323        &mut self,
1324        worktree_id: WorktreeId,
1325        worktree_path: Arc<Path>,
1326        language: Arc<Language>,
1327        cx: &mut ModelContext<Self>,
1328    ) {
1329        let adapter = if let Some(adapter) = language.lsp_adapter() {
1330            adapter
1331        } else {
1332            return;
1333        };
1334        let key = (worktree_id, adapter.name());
1335        self.started_language_servers
1336            .entry(key.clone())
1337            .or_insert_with(|| {
1338                let server_id = post_inc(&mut self.next_language_server_id);
1339                let language_server = self.languages.start_language_server(
1340                    server_id,
1341                    language.clone(),
1342                    worktree_path,
1343                    self.client.http_client(),
1344                    cx,
1345                );
1346                cx.spawn_weak(|this, mut cx| async move {
1347                    let language_server = language_server?.await.log_err()?;
1348                    let language_server = language_server
1349                        .initialize(adapter.initialization_options())
1350                        .await
1351                        .log_err()?;
1352                    let this = this.upgrade(&cx)?;
1353                    let disk_based_diagnostics_progress_token =
1354                        adapter.disk_based_diagnostics_progress_token();
1355
1356                    language_server
1357                        .on_notification::<lsp::notification::PublishDiagnostics, _>({
1358                            let this = this.downgrade();
1359                            let adapter = adapter.clone();
1360                            move |params, mut cx| {
1361                                if let Some(this) = this.upgrade(&cx) {
1362                                    this.update(&mut cx, |this, cx| {
1363                                        this.on_lsp_diagnostics_published(
1364                                            server_id,
1365                                            params,
1366                                            &adapter,
1367                                            disk_based_diagnostics_progress_token,
1368                                            cx,
1369                                        );
1370                                    });
1371                                }
1372                            }
1373                        })
1374                        .detach();
1375
1376                    language_server
1377                        .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
1378                            let settings = this
1379                                .read_with(&cx, |this, _| this.language_server_settings.clone());
1380                            move |params, _| {
1381                                let settings = settings.lock().clone();
1382                                async move {
1383                                    Ok(params
1384                                        .items
1385                                        .into_iter()
1386                                        .map(|item| {
1387                                            if let Some(section) = &item.section {
1388                                                settings
1389                                                    .get(section)
1390                                                    .cloned()
1391                                                    .unwrap_or(serde_json::Value::Null)
1392                                            } else {
1393                                                settings.clone()
1394                                            }
1395                                        })
1396                                        .collect())
1397                                }
1398                            }
1399                        })
1400                        .detach();
1401
1402                    language_server
1403                        .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
1404                            let this = this.downgrade();
1405                            let adapter = adapter.clone();
1406                            let language_server = language_server.clone();
1407                            move |params, cx| {
1408                                Self::on_lsp_workspace_edit(
1409                                    this,
1410                                    params,
1411                                    server_id,
1412                                    adapter.clone(),
1413                                    language_server.clone(),
1414                                    cx,
1415                                )
1416                            }
1417                        })
1418                        .detach();
1419
1420                    language_server
1421                        .on_notification::<lsp::notification::Progress, _>({
1422                            let this = this.downgrade();
1423                            move |params, mut cx| {
1424                                if let Some(this) = this.upgrade(&cx) {
1425                                    this.update(&mut cx, |this, cx| {
1426                                        this.on_lsp_progress(
1427                                            params,
1428                                            server_id,
1429                                            disk_based_diagnostics_progress_token,
1430                                            cx,
1431                                        );
1432                                    });
1433                                }
1434                            }
1435                        })
1436                        .detach();
1437
1438                    this.update(&mut cx, |this, cx| {
1439                        this.language_servers
1440                            .insert(key.clone(), (adapter, language_server.clone()));
1441                        this.language_server_statuses.insert(
1442                            server_id,
1443                            LanguageServerStatus {
1444                                name: language_server.name().to_string(),
1445                                pending_work: Default::default(),
1446                                pending_diagnostic_updates: 0,
1447                            },
1448                        );
1449                        language_server
1450                            .notify::<lsp::notification::DidChangeConfiguration>(
1451                                lsp::DidChangeConfigurationParams {
1452                                    settings: this.language_server_settings.lock().clone(),
1453                                },
1454                            )
1455                            .ok();
1456
1457                        if let Some(project_id) = this.remote_id() {
1458                            this.client
1459                                .send(proto::StartLanguageServer {
1460                                    project_id,
1461                                    server: Some(proto::LanguageServer {
1462                                        id: server_id as u64,
1463                                        name: language_server.name().to_string(),
1464                                    }),
1465                                })
1466                                .log_err();
1467                        }
1468
1469                        // Tell the language server about every open buffer in the worktree that matches the language.
1470                        for buffer in this.opened_buffers.values() {
1471                            if let Some(buffer_handle) = buffer.upgrade(cx) {
1472                                let buffer = buffer_handle.read(cx);
1473                                let file = if let Some(file) = File::from_dyn(buffer.file()) {
1474                                    file
1475                                } else {
1476                                    continue;
1477                                };
1478                                let language = if let Some(language) = buffer.language() {
1479                                    language
1480                                } else {
1481                                    continue;
1482                                };
1483                                if file.worktree.read(cx).id() != key.0
1484                                    || language.lsp_adapter().map(|a| a.name())
1485                                        != Some(key.1.clone())
1486                                {
1487                                    continue;
1488                                }
1489
1490                                let file = file.as_local()?;
1491                                let versions = this
1492                                    .buffer_snapshots
1493                                    .entry(buffer.remote_id())
1494                                    .or_insert_with(|| vec![(0, buffer.text_snapshot())]);
1495                                let (version, initial_snapshot) = versions.last().unwrap();
1496                                let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
1497                                language_server
1498                                    .notify::<lsp::notification::DidOpenTextDocument>(
1499                                        lsp::DidOpenTextDocumentParams {
1500                                            text_document: lsp::TextDocumentItem::new(
1501                                                uri,
1502                                                Default::default(),
1503                                                *version,
1504                                                initial_snapshot.text(),
1505                                            ),
1506                                        },
1507                                    )
1508                                    .log_err()?;
1509                                buffer_handle.update(cx, |buffer, cx| {
1510                                    buffer.set_completion_triggers(
1511                                        language_server
1512                                            .capabilities()
1513                                            .completion_provider
1514                                            .as_ref()
1515                                            .and_then(|provider| {
1516                                                provider.trigger_characters.clone()
1517                                            })
1518                                            .unwrap_or(Vec::new()),
1519                                        cx,
1520                                    )
1521                                });
1522                            }
1523                        }
1524
1525                        cx.notify();
1526                        Some(())
1527                    });
1528
1529                    Some(language_server)
1530                })
1531            });
1532    }
1533
1534    pub fn restart_language_servers_for_buffers(
1535        &mut self,
1536        buffers: impl IntoIterator<Item = ModelHandle<Buffer>>,
1537        cx: &mut ModelContext<Self>,
1538    ) -> Option<()> {
1539        let language_server_lookup_info: HashSet<(WorktreeId, Arc<Path>, PathBuf)> = buffers
1540            .into_iter()
1541            .filter_map(|buffer| {
1542                let file = File::from_dyn(buffer.read(cx).file())?;
1543                let worktree = file.worktree.read(cx).as_local()?;
1544                let worktree_id = worktree.id();
1545                let worktree_abs_path = worktree.abs_path().clone();
1546                let full_path = file.full_path(cx);
1547                Some((worktree_id, worktree_abs_path, full_path))
1548            })
1549            .collect();
1550        for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info {
1551            let language = self.languages.select_language(&full_path)?;
1552            self.restart_language_server(worktree_id, worktree_abs_path, language, cx);
1553        }
1554
1555        None
1556    }
1557
1558    fn restart_language_server(
1559        &mut self,
1560        worktree_id: WorktreeId,
1561        worktree_path: Arc<Path>,
1562        language: Arc<Language>,
1563        cx: &mut ModelContext<Self>,
1564    ) {
1565        let adapter = if let Some(adapter) = language.lsp_adapter() {
1566            adapter
1567        } else {
1568            return;
1569        };
1570        let key = (worktree_id, adapter.name());
1571        let server_to_shutdown = self.language_servers.remove(&key);
1572        self.started_language_servers.remove(&key);
1573        server_to_shutdown
1574            .as_ref()
1575            .map(|(_, server)| self.language_server_statuses.remove(&server.server_id()));
1576        cx.spawn_weak(|this, mut cx| async move {
1577            if let Some(this) = this.upgrade(&cx) {
1578                if let Some((_, server_to_shutdown)) = server_to_shutdown {
1579                    if let Some(shutdown_task) = server_to_shutdown.shutdown() {
1580                        shutdown_task.await;
1581                    }
1582                }
1583
1584                this.update(&mut cx, |this, cx| {
1585                    this.start_language_server(worktree_id, worktree_path, language, cx);
1586                });
1587            }
1588        })
1589        .detach();
1590    }
1591
1592    fn on_lsp_diagnostics_published(
1593        &mut self,
1594        server_id: usize,
1595        mut params: lsp::PublishDiagnosticsParams,
1596        adapter: &Arc<dyn LspAdapter>,
1597        disk_based_diagnostics_progress_token: Option<&str>,
1598        cx: &mut ModelContext<Self>,
1599    ) {
1600        adapter.process_diagnostics(&mut params);
1601        if disk_based_diagnostics_progress_token.is_none() {
1602            self.disk_based_diagnostics_started(cx);
1603            self.broadcast_language_server_update(
1604                server_id,
1605                proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
1606                    proto::LspDiskBasedDiagnosticsUpdating {},
1607                ),
1608            );
1609        }
1610        self.update_diagnostics(params, adapter.disk_based_diagnostic_sources(), cx)
1611            .log_err();
1612        if disk_based_diagnostics_progress_token.is_none() {
1613            self.disk_based_diagnostics_finished(cx);
1614            self.broadcast_language_server_update(
1615                server_id,
1616                proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
1617                    proto::LspDiskBasedDiagnosticsUpdated {},
1618                ),
1619            );
1620        }
1621    }
1622
1623    fn on_lsp_progress(
1624        &mut self,
1625        progress: lsp::ProgressParams,
1626        server_id: usize,
1627        disk_based_diagnostics_progress_token: Option<&str>,
1628        cx: &mut ModelContext<Self>,
1629    ) {
1630        let token = match progress.token {
1631            lsp::NumberOrString::String(token) => token,
1632            lsp::NumberOrString::Number(token) => {
1633                log::info!("skipping numeric progress token {}", token);
1634                return;
1635            }
1636        };
1637        let progress = match progress.value {
1638            lsp::ProgressParamsValue::WorkDone(value) => value,
1639        };
1640        let language_server_status =
1641            if let Some(status) = self.language_server_statuses.get_mut(&server_id) {
1642                status
1643            } else {
1644                return;
1645            };
1646        match progress {
1647            lsp::WorkDoneProgress::Begin(_) => {
1648                if Some(token.as_str()) == disk_based_diagnostics_progress_token {
1649                    language_server_status.pending_diagnostic_updates += 1;
1650                    if language_server_status.pending_diagnostic_updates == 1 {
1651                        self.disk_based_diagnostics_started(cx);
1652                        self.broadcast_language_server_update(
1653                            server_id,
1654                            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
1655                                proto::LspDiskBasedDiagnosticsUpdating {},
1656                            ),
1657                        );
1658                    }
1659                } else {
1660                    self.on_lsp_work_start(server_id, token.clone(), cx);
1661                    self.broadcast_language_server_update(
1662                        server_id,
1663                        proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
1664                            token,
1665                        }),
1666                    );
1667                }
1668            }
1669            lsp::WorkDoneProgress::Report(report) => {
1670                if Some(token.as_str()) != disk_based_diagnostics_progress_token {
1671                    self.on_lsp_work_progress(
1672                        server_id,
1673                        token.clone(),
1674                        LanguageServerProgress {
1675                            message: report.message.clone(),
1676                            percentage: report.percentage.map(|p| p as usize),
1677                            last_update_at: Instant::now(),
1678                        },
1679                        cx,
1680                    );
1681                    self.broadcast_language_server_update(
1682                        server_id,
1683                        proto::update_language_server::Variant::WorkProgress(
1684                            proto::LspWorkProgress {
1685                                token,
1686                                message: report.message,
1687                                percentage: report.percentage.map(|p| p as u32),
1688                            },
1689                        ),
1690                    );
1691                }
1692            }
1693            lsp::WorkDoneProgress::End(_) => {
1694                if Some(token.as_str()) == disk_based_diagnostics_progress_token {
1695                    language_server_status.pending_diagnostic_updates -= 1;
1696                    if language_server_status.pending_diagnostic_updates == 0 {
1697                        self.disk_based_diagnostics_finished(cx);
1698                        self.broadcast_language_server_update(
1699                            server_id,
1700                            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
1701                                proto::LspDiskBasedDiagnosticsUpdated {},
1702                            ),
1703                        );
1704                    }
1705                } else {
1706                    self.on_lsp_work_end(server_id, token.clone(), cx);
1707                    self.broadcast_language_server_update(
1708                        server_id,
1709                        proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd {
1710                            token,
1711                        }),
1712                    );
1713                }
1714            }
1715        }
1716    }
1717
1718    fn on_lsp_work_start(
1719        &mut self,
1720        language_server_id: usize,
1721        token: String,
1722        cx: &mut ModelContext<Self>,
1723    ) {
1724        if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
1725            status.pending_work.insert(
1726                token,
1727                LanguageServerProgress {
1728                    message: None,
1729                    percentage: None,
1730                    last_update_at: Instant::now(),
1731                },
1732            );
1733            cx.notify();
1734        }
1735    }
1736
1737    fn on_lsp_work_progress(
1738        &mut self,
1739        language_server_id: usize,
1740        token: String,
1741        progress: LanguageServerProgress,
1742        cx: &mut ModelContext<Self>,
1743    ) {
1744        if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
1745            status.pending_work.insert(token, progress);
1746            cx.notify();
1747        }
1748    }
1749
1750    fn on_lsp_work_end(
1751        &mut self,
1752        language_server_id: usize,
1753        token: String,
1754        cx: &mut ModelContext<Self>,
1755    ) {
1756        if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
1757            status.pending_work.remove(&token);
1758            cx.notify();
1759        }
1760    }
1761
1762    async fn on_lsp_workspace_edit(
1763        this: WeakModelHandle<Self>,
1764        params: lsp::ApplyWorkspaceEditParams,
1765        server_id: usize,
1766        adapter: Arc<dyn LspAdapter>,
1767        language_server: Arc<LanguageServer>,
1768        mut cx: AsyncAppContext,
1769    ) -> Result<lsp::ApplyWorkspaceEditResponse> {
1770        let this = this
1771            .upgrade(&cx)
1772            .ok_or_else(|| anyhow!("project project closed"))?;
1773        let transaction = Self::deserialize_workspace_edit(
1774            this.clone(),
1775            params.edit,
1776            true,
1777            adapter.clone(),
1778            language_server.clone(),
1779            &mut cx,
1780        )
1781        .await
1782        .log_err();
1783        this.update(&mut cx, |this, _| {
1784            if let Some(transaction) = transaction {
1785                this.last_workspace_edits_by_language_server
1786                    .insert(server_id, transaction);
1787            }
1788        });
1789        Ok(lsp::ApplyWorkspaceEditResponse {
1790            applied: true,
1791            failed_change: None,
1792            failure_reason: None,
1793        })
1794    }
1795
1796    fn broadcast_language_server_update(
1797        &self,
1798        language_server_id: usize,
1799        event: proto::update_language_server::Variant,
1800    ) {
1801        if let Some(project_id) = self.remote_id() {
1802            self.client
1803                .send(proto::UpdateLanguageServer {
1804                    project_id,
1805                    language_server_id: language_server_id as u64,
1806                    variant: Some(event),
1807                })
1808                .log_err();
1809        }
1810    }
1811
1812    pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
1813        for (_, server) in self.language_servers.values() {
1814            server
1815                .notify::<lsp::notification::DidChangeConfiguration>(
1816                    lsp::DidChangeConfigurationParams {
1817                        settings: settings.clone(),
1818                    },
1819                )
1820                .ok();
1821        }
1822        *self.language_server_settings.lock() = settings;
1823    }
1824
1825    pub fn language_server_statuses(
1826        &self,
1827    ) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
1828        self.language_server_statuses.values()
1829    }
1830
1831    pub fn update_diagnostics(
1832        &mut self,
1833        params: lsp::PublishDiagnosticsParams,
1834        disk_based_sources: &[&str],
1835        cx: &mut ModelContext<Self>,
1836    ) -> Result<()> {
1837        let abs_path = params
1838            .uri
1839            .to_file_path()
1840            .map_err(|_| anyhow!("URI is not a file"))?;
1841        let mut next_group_id = 0;
1842        let mut diagnostics = Vec::default();
1843        let mut primary_diagnostic_group_ids = HashMap::default();
1844        let mut sources_by_group_id = HashMap::default();
1845        let mut supporting_diagnostics = HashMap::default();
1846        for diagnostic in &params.diagnostics {
1847            let source = diagnostic.source.as_ref();
1848            let code = diagnostic.code.as_ref().map(|code| match code {
1849                lsp::NumberOrString::Number(code) => code.to_string(),
1850                lsp::NumberOrString::String(code) => code.clone(),
1851            });
1852            let range = range_from_lsp(diagnostic.range);
1853            let is_supporting = diagnostic
1854                .related_information
1855                .as_ref()
1856                .map_or(false, |infos| {
1857                    infos.iter().any(|info| {
1858                        primary_diagnostic_group_ids.contains_key(&(
1859                            source,
1860                            code.clone(),
1861                            range_from_lsp(info.location.range),
1862                        ))
1863                    })
1864                });
1865
1866            let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
1867                tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
1868            });
1869
1870            if is_supporting {
1871                supporting_diagnostics.insert(
1872                    (source, code.clone(), range),
1873                    (diagnostic.severity, is_unnecessary),
1874                );
1875            } else {
1876                let group_id = post_inc(&mut next_group_id);
1877                let is_disk_based = source.map_or(false, |source| {
1878                    disk_based_sources.contains(&source.as_str())
1879                });
1880
1881                sources_by_group_id.insert(group_id, source);
1882                primary_diagnostic_group_ids
1883                    .insert((source, code.clone(), range.clone()), group_id);
1884
1885                diagnostics.push(DiagnosticEntry {
1886                    range,
1887                    diagnostic: Diagnostic {
1888                        code: code.clone(),
1889                        severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
1890                        message: diagnostic.message.clone(),
1891                        group_id,
1892                        is_primary: true,
1893                        is_valid: true,
1894                        is_disk_based,
1895                        is_unnecessary,
1896                    },
1897                });
1898                if let Some(infos) = &diagnostic.related_information {
1899                    for info in infos {
1900                        if info.location.uri == params.uri && !info.message.is_empty() {
1901                            let range = range_from_lsp(info.location.range);
1902                            diagnostics.push(DiagnosticEntry {
1903                                range,
1904                                diagnostic: Diagnostic {
1905                                    code: code.clone(),
1906                                    severity: DiagnosticSeverity::INFORMATION,
1907                                    message: info.message.clone(),
1908                                    group_id,
1909                                    is_primary: false,
1910                                    is_valid: true,
1911                                    is_disk_based,
1912                                    is_unnecessary: false,
1913                                },
1914                            });
1915                        }
1916                    }
1917                }
1918            }
1919        }
1920
1921        for entry in &mut diagnostics {
1922            let diagnostic = &mut entry.diagnostic;
1923            if !diagnostic.is_primary {
1924                let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
1925                if let Some(&(severity, is_unnecessary)) = supporting_diagnostics.get(&(
1926                    source,
1927                    diagnostic.code.clone(),
1928                    entry.range.clone(),
1929                )) {
1930                    if let Some(severity) = severity {
1931                        diagnostic.severity = severity;
1932                    }
1933                    diagnostic.is_unnecessary = is_unnecessary;
1934                }
1935            }
1936        }
1937
1938        self.update_diagnostic_entries(abs_path, params.version, diagnostics, cx)?;
1939        Ok(())
1940    }
1941
1942    pub fn update_diagnostic_entries(
1943        &mut self,
1944        abs_path: PathBuf,
1945        version: Option<i32>,
1946        diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
1947        cx: &mut ModelContext<Project>,
1948    ) -> Result<(), anyhow::Error> {
1949        let (worktree, relative_path) = self
1950            .find_local_worktree(&abs_path, cx)
1951            .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
1952        if !worktree.read(cx).is_visible() {
1953            return Ok(());
1954        }
1955
1956        let project_path = ProjectPath {
1957            worktree_id: worktree.read(cx).id(),
1958            path: relative_path.into(),
1959        };
1960        if let Some(buffer) = self.get_open_buffer(&project_path, cx) {
1961            self.update_buffer_diagnostics(&buffer, diagnostics.clone(), version, cx)?;
1962        }
1963
1964        let updated = worktree.update(cx, |worktree, cx| {
1965            worktree
1966                .as_local_mut()
1967                .ok_or_else(|| anyhow!("not a local worktree"))?
1968                .update_diagnostics(project_path.path.clone(), diagnostics, cx)
1969        })?;
1970        if updated {
1971            cx.emit(Event::DiagnosticsUpdated(project_path));
1972        }
1973        Ok(())
1974    }
1975
1976    fn update_buffer_diagnostics(
1977        &mut self,
1978        buffer: &ModelHandle<Buffer>,
1979        mut diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
1980        version: Option<i32>,
1981        cx: &mut ModelContext<Self>,
1982    ) -> Result<()> {
1983        fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
1984            Ordering::Equal
1985                .then_with(|| b.is_primary.cmp(&a.is_primary))
1986                .then_with(|| a.is_disk_based.cmp(&b.is_disk_based))
1987                .then_with(|| a.severity.cmp(&b.severity))
1988                .then_with(|| a.message.cmp(&b.message))
1989        }
1990
1991        let snapshot = self.buffer_snapshot_for_lsp_version(buffer, version, cx)?;
1992
1993        diagnostics.sort_unstable_by(|a, b| {
1994            Ordering::Equal
1995                .then_with(|| a.range.start.cmp(&b.range.start))
1996                .then_with(|| b.range.end.cmp(&a.range.end))
1997                .then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
1998        });
1999
2000        let mut sanitized_diagnostics = Vec::new();
2001        let edits_since_save = Patch::new(
2002            snapshot
2003                .edits_since::<PointUtf16>(buffer.read(cx).saved_version())
2004                .collect(),
2005        );
2006        for entry in diagnostics {
2007            let start;
2008            let end;
2009            if entry.diagnostic.is_disk_based {
2010                // Some diagnostics are based on files on disk instead of buffers'
2011                // current contents. Adjust these diagnostics' ranges to reflect
2012                // any unsaved edits.
2013                start = edits_since_save.old_to_new(entry.range.start);
2014                end = edits_since_save.old_to_new(entry.range.end);
2015            } else {
2016                start = entry.range.start;
2017                end = entry.range.end;
2018            }
2019
2020            let mut range = snapshot.clip_point_utf16(start, Bias::Left)
2021                ..snapshot.clip_point_utf16(end, Bias::Right);
2022
2023            // Expand empty ranges by one character
2024            if range.start == range.end {
2025                range.end.column += 1;
2026                range.end = snapshot.clip_point_utf16(range.end, Bias::Right);
2027                if range.start == range.end && range.end.column > 0 {
2028                    range.start.column -= 1;
2029                    range.start = snapshot.clip_point_utf16(range.start, Bias::Left);
2030                }
2031            }
2032
2033            sanitized_diagnostics.push(DiagnosticEntry {
2034                range,
2035                diagnostic: entry.diagnostic,
2036            });
2037        }
2038        drop(edits_since_save);
2039
2040        let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
2041        buffer.update(cx, |buffer, cx| buffer.update_diagnostics(set, cx));
2042        Ok(())
2043    }
2044
2045    pub fn reload_buffers(
2046        &self,
2047        buffers: HashSet<ModelHandle<Buffer>>,
2048        push_to_history: bool,
2049        cx: &mut ModelContext<Self>,
2050    ) -> Task<Result<ProjectTransaction>> {
2051        let mut local_buffers = Vec::new();
2052        let mut remote_buffers = None;
2053        for buffer_handle in buffers {
2054            let buffer = buffer_handle.read(cx);
2055            if buffer.is_dirty() {
2056                if let Some(file) = File::from_dyn(buffer.file()) {
2057                    if file.is_local() {
2058                        local_buffers.push(buffer_handle);
2059                    } else {
2060                        remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
2061                    }
2062                }
2063            }
2064        }
2065
2066        let remote_buffers = self.remote_id().zip(remote_buffers);
2067        let client = self.client.clone();
2068
2069        cx.spawn(|this, mut cx| async move {
2070            let mut project_transaction = ProjectTransaction::default();
2071
2072            if let Some((project_id, remote_buffers)) = remote_buffers {
2073                let response = client
2074                    .request(proto::ReloadBuffers {
2075                        project_id,
2076                        buffer_ids: remote_buffers
2077                            .iter()
2078                            .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
2079                            .collect(),
2080                    })
2081                    .await?
2082                    .transaction
2083                    .ok_or_else(|| anyhow!("missing transaction"))?;
2084                project_transaction = this
2085                    .update(&mut cx, |this, cx| {
2086                        this.deserialize_project_transaction(response, push_to_history, cx)
2087                    })
2088                    .await?;
2089            }
2090
2091            for buffer in local_buffers {
2092                let transaction = buffer
2093                    .update(&mut cx, |buffer, cx| buffer.reload(cx))
2094                    .await?;
2095                buffer.update(&mut cx, |buffer, cx| {
2096                    if let Some(transaction) = transaction {
2097                        if !push_to_history {
2098                            buffer.forget_transaction(transaction.id);
2099                        }
2100                        project_transaction.0.insert(cx.handle(), transaction);
2101                    }
2102                });
2103            }
2104
2105            Ok(project_transaction)
2106        })
2107    }
2108
2109    pub fn format(
2110        &self,
2111        buffers: HashSet<ModelHandle<Buffer>>,
2112        push_to_history: bool,
2113        cx: &mut ModelContext<Project>,
2114    ) -> Task<Result<ProjectTransaction>> {
2115        let mut local_buffers = Vec::new();
2116        let mut remote_buffers = None;
2117        for buffer_handle in buffers {
2118            let buffer = buffer_handle.read(cx);
2119            if let Some(file) = File::from_dyn(buffer.file()) {
2120                if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
2121                    if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) {
2122                        local_buffers.push((buffer_handle, buffer_abs_path, server.clone()));
2123                    }
2124                } else {
2125                    remote_buffers.get_or_insert(Vec::new()).push(buffer_handle);
2126                }
2127            } else {
2128                return Task::ready(Ok(Default::default()));
2129            }
2130        }
2131
2132        let remote_buffers = self.remote_id().zip(remote_buffers);
2133        let client = self.client.clone();
2134
2135        cx.spawn(|this, mut cx| async move {
2136            let mut project_transaction = ProjectTransaction::default();
2137
2138            if let Some((project_id, remote_buffers)) = remote_buffers {
2139                let response = client
2140                    .request(proto::FormatBuffers {
2141                        project_id,
2142                        buffer_ids: remote_buffers
2143                            .iter()
2144                            .map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
2145                            .collect(),
2146                    })
2147                    .await?
2148                    .transaction
2149                    .ok_or_else(|| anyhow!("missing transaction"))?;
2150                project_transaction = this
2151                    .update(&mut cx, |this, cx| {
2152                        this.deserialize_project_transaction(response, push_to_history, cx)
2153                    })
2154                    .await?;
2155            }
2156
2157            for (buffer, buffer_abs_path, language_server) in local_buffers {
2158                let text_document = lsp::TextDocumentIdentifier::new(
2159                    lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
2160                );
2161                let capabilities = &language_server.capabilities();
2162                let tab_size = cx.update(|cx| {
2163                    let language_name = buffer.read(cx).language().map(|language| language.name());
2164                    cx.global::<Settings>().tab_size(language_name.as_deref())
2165                });
2166                let lsp_edits = if capabilities
2167                    .document_formatting_provider
2168                    .as_ref()
2169                    .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
2170                {
2171                    language_server
2172                        .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
2173                            text_document,
2174                            options: lsp::FormattingOptions {
2175                                tab_size,
2176                                insert_spaces: true,
2177                                insert_final_newline: Some(true),
2178                                ..Default::default()
2179                            },
2180                            work_done_progress_params: Default::default(),
2181                        })
2182                        .await?
2183                } else if capabilities
2184                    .document_range_formatting_provider
2185                    .as_ref()
2186                    .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
2187                {
2188                    let buffer_start = lsp::Position::new(0, 0);
2189                    let buffer_end =
2190                        buffer.read_with(&cx, |buffer, _| point_to_lsp(buffer.max_point_utf16()));
2191                    language_server
2192                        .request::<lsp::request::RangeFormatting>(
2193                            lsp::DocumentRangeFormattingParams {
2194                                text_document,
2195                                range: lsp::Range::new(buffer_start, buffer_end),
2196                                options: lsp::FormattingOptions {
2197                                    tab_size: 4,
2198                                    insert_spaces: true,
2199                                    insert_final_newline: Some(true),
2200                                    ..Default::default()
2201                                },
2202                                work_done_progress_params: Default::default(),
2203                            },
2204                        )
2205                        .await?
2206                } else {
2207                    continue;
2208                };
2209
2210                if let Some(lsp_edits) = lsp_edits {
2211                    let edits = this
2212                        .update(&mut cx, |this, cx| {
2213                            this.edits_from_lsp(&buffer, lsp_edits, None, cx)
2214                        })
2215                        .await?;
2216                    buffer.update(&mut cx, |buffer, cx| {
2217                        buffer.finalize_last_transaction();
2218                        buffer.start_transaction();
2219                        for (range, text) in edits {
2220                            buffer.edit([range], text, cx);
2221                        }
2222                        if buffer.end_transaction(cx).is_some() {
2223                            let transaction = buffer.finalize_last_transaction().unwrap().clone();
2224                            if !push_to_history {
2225                                buffer.forget_transaction(transaction.id);
2226                            }
2227                            project_transaction.0.insert(cx.handle(), transaction);
2228                        }
2229                    });
2230                }
2231            }
2232
2233            Ok(project_transaction)
2234        })
2235    }
2236
2237    pub fn definition<T: ToPointUtf16>(
2238        &self,
2239        buffer: &ModelHandle<Buffer>,
2240        position: T,
2241        cx: &mut ModelContext<Self>,
2242    ) -> Task<Result<Vec<Location>>> {
2243        let position = position.to_point_utf16(buffer.read(cx));
2244        self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
2245    }
2246
2247    pub fn references<T: ToPointUtf16>(
2248        &self,
2249        buffer: &ModelHandle<Buffer>,
2250        position: T,
2251        cx: &mut ModelContext<Self>,
2252    ) -> Task<Result<Vec<Location>>> {
2253        let position = position.to_point_utf16(buffer.read(cx));
2254        self.request_lsp(buffer.clone(), GetReferences { position }, cx)
2255    }
2256
2257    pub fn document_highlights<T: ToPointUtf16>(
2258        &self,
2259        buffer: &ModelHandle<Buffer>,
2260        position: T,
2261        cx: &mut ModelContext<Self>,
2262    ) -> Task<Result<Vec<DocumentHighlight>>> {
2263        let position = position.to_point_utf16(buffer.read(cx));
2264
2265        self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
2266    }
2267
2268    pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
2269        if self.is_local() {
2270            let mut requests = Vec::new();
2271            for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() {
2272                let worktree_id = *worktree_id;
2273                if let Some(worktree) = self
2274                    .worktree_for_id(worktree_id, cx)
2275                    .and_then(|worktree| worktree.read(cx).as_local())
2276                {
2277                    let lsp_adapter = lsp_adapter.clone();
2278                    let worktree_abs_path = worktree.abs_path().clone();
2279                    requests.push(
2280                        language_server
2281                            .request::<lsp::request::WorkspaceSymbol>(lsp::WorkspaceSymbolParams {
2282                                query: query.to_string(),
2283                                ..Default::default()
2284                            })
2285                            .log_err()
2286                            .map(move |response| {
2287                                (
2288                                    lsp_adapter,
2289                                    worktree_id,
2290                                    worktree_abs_path,
2291                                    response.unwrap_or_default(),
2292                                )
2293                            }),
2294                    );
2295                }
2296            }
2297
2298            cx.spawn_weak(|this, cx| async move {
2299                let responses = futures::future::join_all(requests).await;
2300                let this = if let Some(this) = this.upgrade(&cx) {
2301                    this
2302                } else {
2303                    return Ok(Default::default());
2304                };
2305                this.read_with(&cx, |this, cx| {
2306                    let mut symbols = Vec::new();
2307                    for (adapter, source_worktree_id, worktree_abs_path, response) in responses {
2308                        symbols.extend(response.into_iter().flatten().filter_map(|lsp_symbol| {
2309                            let abs_path = lsp_symbol.location.uri.to_file_path().ok()?;
2310                            let mut worktree_id = source_worktree_id;
2311                            let path;
2312                            if let Some((worktree, rel_path)) =
2313                                this.find_local_worktree(&abs_path, cx)
2314                            {
2315                                worktree_id = worktree.read(cx).id();
2316                                path = rel_path;
2317                            } else {
2318                                path = relativize_path(&worktree_abs_path, &abs_path);
2319                            }
2320
2321                            let label = this
2322                                .languages
2323                                .select_language(&path)
2324                                .and_then(|language| {
2325                                    language.label_for_symbol(&lsp_symbol.name, lsp_symbol.kind)
2326                                })
2327                                .unwrap_or_else(|| CodeLabel::plain(lsp_symbol.name.clone(), None));
2328                            let signature = this.symbol_signature(worktree_id, &path);
2329
2330                            Some(Symbol {
2331                                source_worktree_id,
2332                                worktree_id,
2333                                language_server_name: adapter.name(),
2334                                name: lsp_symbol.name,
2335                                kind: lsp_symbol.kind,
2336                                label,
2337                                path,
2338                                range: range_from_lsp(lsp_symbol.location.range),
2339                                signature,
2340                            })
2341                        }));
2342                    }
2343                    Ok(symbols)
2344                })
2345            })
2346        } else if let Some(project_id) = self.remote_id() {
2347            let request = self.client.request(proto::GetProjectSymbols {
2348                project_id,
2349                query: query.to_string(),
2350            });
2351            cx.spawn_weak(|this, cx| async move {
2352                let response = request.await?;
2353                let mut symbols = Vec::new();
2354                if let Some(this) = this.upgrade(&cx) {
2355                    this.read_with(&cx, |this, _| {
2356                        symbols.extend(
2357                            response
2358                                .symbols
2359                                .into_iter()
2360                                .filter_map(|symbol| this.deserialize_symbol(symbol).log_err()),
2361                        );
2362                    })
2363                }
2364                Ok(symbols)
2365            })
2366        } else {
2367            Task::ready(Ok(Default::default()))
2368        }
2369    }
2370
2371    pub fn open_buffer_for_symbol(
2372        &mut self,
2373        symbol: &Symbol,
2374        cx: &mut ModelContext<Self>,
2375    ) -> Task<Result<ModelHandle<Buffer>>> {
2376        if self.is_local() {
2377            let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&(
2378                symbol.source_worktree_id,
2379                symbol.language_server_name.clone(),
2380            )) {
2381                server.clone()
2382            } else {
2383                return Task::ready(Err(anyhow!(
2384                    "language server for worktree and language not found"
2385                )));
2386            };
2387
2388            let worktree_abs_path = if let Some(worktree_abs_path) = self
2389                .worktree_for_id(symbol.worktree_id, cx)
2390                .and_then(|worktree| worktree.read(cx).as_local())
2391                .map(|local_worktree| local_worktree.abs_path())
2392            {
2393                worktree_abs_path
2394            } else {
2395                return Task::ready(Err(anyhow!("worktree not found for symbol")));
2396            };
2397            let symbol_abs_path = worktree_abs_path.join(&symbol.path);
2398            let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
2399                uri
2400            } else {
2401                return Task::ready(Err(anyhow!("invalid symbol path")));
2402            };
2403
2404            self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx)
2405        } else if let Some(project_id) = self.remote_id() {
2406            let request = self.client.request(proto::OpenBufferForSymbol {
2407                project_id,
2408                symbol: Some(serialize_symbol(symbol)),
2409            });
2410            cx.spawn(|this, mut cx| async move {
2411                let response = request.await?;
2412                let buffer = response.buffer.ok_or_else(|| anyhow!("invalid buffer"))?;
2413                this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
2414                    .await
2415            })
2416        } else {
2417            Task::ready(Err(anyhow!("project does not have a remote id")))
2418        }
2419    }
2420
2421    pub fn completions<T: ToPointUtf16>(
2422        &self,
2423        source_buffer_handle: &ModelHandle<Buffer>,
2424        position: T,
2425        cx: &mut ModelContext<Self>,
2426    ) -> Task<Result<Vec<Completion>>> {
2427        let source_buffer_handle = source_buffer_handle.clone();
2428        let source_buffer = source_buffer_handle.read(cx);
2429        let buffer_id = source_buffer.remote_id();
2430        let language = source_buffer.language().cloned();
2431        let worktree;
2432        let buffer_abs_path;
2433        if let Some(file) = File::from_dyn(source_buffer.file()) {
2434            worktree = file.worktree.clone();
2435            buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
2436        } else {
2437            return Task::ready(Ok(Default::default()));
2438        };
2439
2440        let position = position.to_point_utf16(source_buffer);
2441        let anchor = source_buffer.anchor_after(position);
2442
2443        if worktree.read(cx).as_local().is_some() {
2444            let buffer_abs_path = buffer_abs_path.unwrap();
2445            let (_, lang_server) =
2446                if let Some(server) = self.language_server_for_buffer(source_buffer, cx) {
2447                    server.clone()
2448                } else {
2449                    return Task::ready(Ok(Default::default()));
2450                };
2451
2452            cx.spawn(|_, cx| async move {
2453                let completions = lang_server
2454                    .request::<lsp::request::Completion>(lsp::CompletionParams {
2455                        text_document_position: lsp::TextDocumentPositionParams::new(
2456                            lsp::TextDocumentIdentifier::new(
2457                                lsp::Url::from_file_path(buffer_abs_path).unwrap(),
2458                            ),
2459                            point_to_lsp(position),
2460                        ),
2461                        context: Default::default(),
2462                        work_done_progress_params: Default::default(),
2463                        partial_result_params: Default::default(),
2464                    })
2465                    .await
2466                    .context("lsp completion request failed")?;
2467
2468                let completions = if let Some(completions) = completions {
2469                    match completions {
2470                        lsp::CompletionResponse::Array(completions) => completions,
2471                        lsp::CompletionResponse::List(list) => list.items,
2472                    }
2473                } else {
2474                    Default::default()
2475                };
2476
2477                source_buffer_handle.read_with(&cx, |this, _| {
2478                    Ok(completions
2479                        .into_iter()
2480                        .filter_map(|lsp_completion| {
2481                            let (old_range, new_text) = match lsp_completion.text_edit.as_ref() {
2482                                Some(lsp::CompletionTextEdit::Edit(edit)) => {
2483                                    (range_from_lsp(edit.range), edit.new_text.clone())
2484                                }
2485                                None => {
2486                                    let clipped_position =
2487                                        this.clip_point_utf16(position, Bias::Left);
2488                                    if position != clipped_position {
2489                                        log::info!("completion out of expected range");
2490                                        return None;
2491                                    }
2492                                    (
2493                                        this.common_prefix_at(
2494                                            clipped_position,
2495                                            &lsp_completion.label,
2496                                        ),
2497                                        lsp_completion.label.clone(),
2498                                    )
2499                                }
2500                                Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
2501                                    log::info!("unsupported insert/replace completion");
2502                                    return None;
2503                                }
2504                            };
2505
2506                            let clipped_start = this.clip_point_utf16(old_range.start, Bias::Left);
2507                            let clipped_end = this.clip_point_utf16(old_range.end, Bias::Left);
2508                            if clipped_start == old_range.start && clipped_end == old_range.end {
2509                                Some(Completion {
2510                                    old_range: this.anchor_before(old_range.start)
2511                                        ..this.anchor_after(old_range.end),
2512                                    new_text,
2513                                    label: language
2514                                        .as_ref()
2515                                        .and_then(|l| l.label_for_completion(&lsp_completion))
2516                                        .unwrap_or_else(|| {
2517                                            CodeLabel::plain(
2518                                                lsp_completion.label.clone(),
2519                                                lsp_completion.filter_text.as_deref(),
2520                                            )
2521                                        }),
2522                                    lsp_completion,
2523                                })
2524                            } else {
2525                                log::info!("completion out of expected range");
2526                                None
2527                            }
2528                        })
2529                        .collect())
2530                })
2531            })
2532        } else if let Some(project_id) = self.remote_id() {
2533            let rpc = self.client.clone();
2534            let message = proto::GetCompletions {
2535                project_id,
2536                buffer_id,
2537                position: Some(language::proto::serialize_anchor(&anchor)),
2538                version: serialize_version(&source_buffer.version()),
2539            };
2540            cx.spawn_weak(|_, mut cx| async move {
2541                let response = rpc.request(message).await?;
2542
2543                source_buffer_handle
2544                    .update(&mut cx, |buffer, _| {
2545                        buffer.wait_for_version(deserialize_version(response.version))
2546                    })
2547                    .await;
2548
2549                response
2550                    .completions
2551                    .into_iter()
2552                    .map(|completion| {
2553                        language::proto::deserialize_completion(completion, language.as_ref())
2554                    })
2555                    .collect()
2556            })
2557        } else {
2558            Task::ready(Ok(Default::default()))
2559        }
2560    }
2561
2562    pub fn apply_additional_edits_for_completion(
2563        &self,
2564        buffer_handle: ModelHandle<Buffer>,
2565        completion: Completion,
2566        push_to_history: bool,
2567        cx: &mut ModelContext<Self>,
2568    ) -> Task<Result<Option<Transaction>>> {
2569        let buffer = buffer_handle.read(cx);
2570        let buffer_id = buffer.remote_id();
2571
2572        if self.is_local() {
2573            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
2574            {
2575                server.clone()
2576            } else {
2577                return Task::ready(Ok(Default::default()));
2578            };
2579
2580            cx.spawn(|this, mut cx| async move {
2581                let resolved_completion = lang_server
2582                    .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
2583                    .await?;
2584                if let Some(edits) = resolved_completion.additional_text_edits {
2585                    let edits = this
2586                        .update(&mut cx, |this, cx| {
2587                            this.edits_from_lsp(&buffer_handle, edits, None, cx)
2588                        })
2589                        .await?;
2590                    buffer_handle.update(&mut cx, |buffer, cx| {
2591                        buffer.finalize_last_transaction();
2592                        buffer.start_transaction();
2593                        for (range, text) in edits {
2594                            buffer.edit([range], text, cx);
2595                        }
2596                        let transaction = if buffer.end_transaction(cx).is_some() {
2597                            let transaction = buffer.finalize_last_transaction().unwrap().clone();
2598                            if !push_to_history {
2599                                buffer.forget_transaction(transaction.id);
2600                            }
2601                            Some(transaction)
2602                        } else {
2603                            None
2604                        };
2605                        Ok(transaction)
2606                    })
2607                } else {
2608                    Ok(None)
2609                }
2610            })
2611        } else if let Some(project_id) = self.remote_id() {
2612            let client = self.client.clone();
2613            cx.spawn(|_, mut cx| async move {
2614                let response = client
2615                    .request(proto::ApplyCompletionAdditionalEdits {
2616                        project_id,
2617                        buffer_id,
2618                        completion: Some(language::proto::serialize_completion(&completion)),
2619                    })
2620                    .await?;
2621
2622                if let Some(transaction) = response.transaction {
2623                    let transaction = language::proto::deserialize_transaction(transaction)?;
2624                    buffer_handle
2625                        .update(&mut cx, |buffer, _| {
2626                            buffer.wait_for_edits(transaction.edit_ids.iter().copied())
2627                        })
2628                        .await;
2629                    if push_to_history {
2630                        buffer_handle.update(&mut cx, |buffer, _| {
2631                            buffer.push_transaction(transaction.clone(), Instant::now());
2632                        });
2633                    }
2634                    Ok(Some(transaction))
2635                } else {
2636                    Ok(None)
2637                }
2638            })
2639        } else {
2640            Task::ready(Err(anyhow!("project does not have a remote id")))
2641        }
2642    }
2643
2644    pub fn code_actions<T: Clone + ToOffset>(
2645        &self,
2646        buffer_handle: &ModelHandle<Buffer>,
2647        range: Range<T>,
2648        cx: &mut ModelContext<Self>,
2649    ) -> Task<Result<Vec<CodeAction>>> {
2650        let buffer_handle = buffer_handle.clone();
2651        let buffer = buffer_handle.read(cx);
2652        let snapshot = buffer.snapshot();
2653        let relevant_diagnostics = snapshot
2654            .diagnostics_in_range::<usize, usize>(range.to_offset(&snapshot), false)
2655            .map(|entry| entry.to_lsp_diagnostic_stub())
2656            .collect();
2657        let buffer_id = buffer.remote_id();
2658        let worktree;
2659        let buffer_abs_path;
2660        if let Some(file) = File::from_dyn(buffer.file()) {
2661            worktree = file.worktree.clone();
2662            buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
2663        } else {
2664            return Task::ready(Ok(Default::default()));
2665        };
2666        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
2667
2668        if worktree.read(cx).as_local().is_some() {
2669            let buffer_abs_path = buffer_abs_path.unwrap();
2670            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
2671            {
2672                server.clone()
2673            } else {
2674                return Task::ready(Ok(Default::default()));
2675            };
2676
2677            let lsp_range = range_to_lsp(range.to_point_utf16(buffer));
2678            cx.foreground().spawn(async move {
2679                if !lang_server.capabilities().code_action_provider.is_some() {
2680                    return Ok(Default::default());
2681                }
2682
2683                Ok(lang_server
2684                    .request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
2685                        text_document: lsp::TextDocumentIdentifier::new(
2686                            lsp::Url::from_file_path(buffer_abs_path).unwrap(),
2687                        ),
2688                        range: lsp_range,
2689                        work_done_progress_params: Default::default(),
2690                        partial_result_params: Default::default(),
2691                        context: lsp::CodeActionContext {
2692                            diagnostics: relevant_diagnostics,
2693                            only: Some(vec![
2694                                lsp::CodeActionKind::QUICKFIX,
2695                                lsp::CodeActionKind::REFACTOR,
2696                                lsp::CodeActionKind::REFACTOR_EXTRACT,
2697                                lsp::CodeActionKind::SOURCE,
2698                            ]),
2699                        },
2700                    })
2701                    .await?
2702                    .unwrap_or_default()
2703                    .into_iter()
2704                    .filter_map(|entry| {
2705                        if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
2706                            Some(CodeAction {
2707                                range: range.clone(),
2708                                lsp_action,
2709                            })
2710                        } else {
2711                            None
2712                        }
2713                    })
2714                    .collect())
2715            })
2716        } else if let Some(project_id) = self.remote_id() {
2717            let rpc = self.client.clone();
2718            let version = buffer.version();
2719            cx.spawn_weak(|_, mut cx| async move {
2720                let response = rpc
2721                    .request(proto::GetCodeActions {
2722                        project_id,
2723                        buffer_id,
2724                        start: Some(language::proto::serialize_anchor(&range.start)),
2725                        end: Some(language::proto::serialize_anchor(&range.end)),
2726                        version: serialize_version(&version),
2727                    })
2728                    .await?;
2729
2730                buffer_handle
2731                    .update(&mut cx, |buffer, _| {
2732                        buffer.wait_for_version(deserialize_version(response.version))
2733                    })
2734                    .await;
2735
2736                response
2737                    .actions
2738                    .into_iter()
2739                    .map(language::proto::deserialize_code_action)
2740                    .collect()
2741            })
2742        } else {
2743            Task::ready(Ok(Default::default()))
2744        }
2745    }
2746
2747    pub fn apply_code_action(
2748        &self,
2749        buffer_handle: ModelHandle<Buffer>,
2750        mut action: CodeAction,
2751        push_to_history: bool,
2752        cx: &mut ModelContext<Self>,
2753    ) -> Task<Result<ProjectTransaction>> {
2754        if self.is_local() {
2755            let buffer = buffer_handle.read(cx);
2756            let (lsp_adapter, lang_server) =
2757                if let Some(server) = self.language_server_for_buffer(buffer, cx) {
2758                    server.clone()
2759                } else {
2760                    return Task::ready(Ok(Default::default()));
2761                };
2762            let range = action.range.to_point_utf16(buffer);
2763
2764            cx.spawn(|this, mut cx| async move {
2765                if let Some(lsp_range) = action
2766                    .lsp_action
2767                    .data
2768                    .as_mut()
2769                    .and_then(|d| d.get_mut("codeActionParams"))
2770                    .and_then(|d| d.get_mut("range"))
2771                {
2772                    *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
2773                    action.lsp_action = lang_server
2774                        .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
2775                        .await?;
2776                } else {
2777                    let actions = this
2778                        .update(&mut cx, |this, cx| {
2779                            this.code_actions(&buffer_handle, action.range, cx)
2780                        })
2781                        .await?;
2782                    action.lsp_action = actions
2783                        .into_iter()
2784                        .find(|a| a.lsp_action.title == action.lsp_action.title)
2785                        .ok_or_else(|| anyhow!("code action is outdated"))?
2786                        .lsp_action;
2787                }
2788
2789                if let Some(edit) = action.lsp_action.edit {
2790                    Self::deserialize_workspace_edit(
2791                        this,
2792                        edit,
2793                        push_to_history,
2794                        lsp_adapter,
2795                        lang_server,
2796                        &mut cx,
2797                    )
2798                    .await
2799                } else if let Some(command) = action.lsp_action.command {
2800                    this.update(&mut cx, |this, _| {
2801                        this.last_workspace_edits_by_language_server
2802                            .remove(&lang_server.server_id());
2803                    });
2804                    lang_server
2805                        .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
2806                            command: command.command,
2807                            arguments: command.arguments.unwrap_or_default(),
2808                            ..Default::default()
2809                        })
2810                        .await?;
2811                    Ok(this.update(&mut cx, |this, _| {
2812                        this.last_workspace_edits_by_language_server
2813                            .remove(&lang_server.server_id())
2814                            .unwrap_or_default()
2815                    }))
2816                } else {
2817                    Ok(ProjectTransaction::default())
2818                }
2819            })
2820        } else if let Some(project_id) = self.remote_id() {
2821            let client = self.client.clone();
2822            let request = proto::ApplyCodeAction {
2823                project_id,
2824                buffer_id: buffer_handle.read(cx).remote_id(),
2825                action: Some(language::proto::serialize_code_action(&action)),
2826            };
2827            cx.spawn(|this, mut cx| async move {
2828                let response = client
2829                    .request(request)
2830                    .await?
2831                    .transaction
2832                    .ok_or_else(|| anyhow!("missing transaction"))?;
2833                this.update(&mut cx, |this, cx| {
2834                    this.deserialize_project_transaction(response, push_to_history, cx)
2835                })
2836                .await
2837            })
2838        } else {
2839            Task::ready(Err(anyhow!("project does not have a remote id")))
2840        }
2841    }
2842
2843    async fn deserialize_workspace_edit(
2844        this: ModelHandle<Self>,
2845        edit: lsp::WorkspaceEdit,
2846        push_to_history: bool,
2847        lsp_adapter: Arc<dyn LspAdapter>,
2848        language_server: Arc<LanguageServer>,
2849        cx: &mut AsyncAppContext,
2850    ) -> Result<ProjectTransaction> {
2851        let fs = this.read_with(cx, |this, _| this.fs.clone());
2852        let mut operations = Vec::new();
2853        if let Some(document_changes) = edit.document_changes {
2854            match document_changes {
2855                lsp::DocumentChanges::Edits(edits) => {
2856                    operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
2857                }
2858                lsp::DocumentChanges::Operations(ops) => operations = ops,
2859            }
2860        } else if let Some(changes) = edit.changes {
2861            operations.extend(changes.into_iter().map(|(uri, edits)| {
2862                lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
2863                    text_document: lsp::OptionalVersionedTextDocumentIdentifier {
2864                        uri,
2865                        version: None,
2866                    },
2867                    edits: edits.into_iter().map(lsp::OneOf::Left).collect(),
2868                })
2869            }));
2870        }
2871
2872        let mut project_transaction = ProjectTransaction::default();
2873        for operation in operations {
2874            match operation {
2875                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
2876                    let abs_path = op
2877                        .uri
2878                        .to_file_path()
2879                        .map_err(|_| anyhow!("can't convert URI to path"))?;
2880
2881                    if let Some(parent_path) = abs_path.parent() {
2882                        fs.create_dir(parent_path).await?;
2883                    }
2884                    if abs_path.ends_with("/") {
2885                        fs.create_dir(&abs_path).await?;
2886                    } else {
2887                        fs.create_file(&abs_path, op.options.map(Into::into).unwrap_or_default())
2888                            .await?;
2889                    }
2890                }
2891                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
2892                    let source_abs_path = op
2893                        .old_uri
2894                        .to_file_path()
2895                        .map_err(|_| anyhow!("can't convert URI to path"))?;
2896                    let target_abs_path = op
2897                        .new_uri
2898                        .to_file_path()
2899                        .map_err(|_| anyhow!("can't convert URI to path"))?;
2900                    fs.rename(
2901                        &source_abs_path,
2902                        &target_abs_path,
2903                        op.options.map(Into::into).unwrap_or_default(),
2904                    )
2905                    .await?;
2906                }
2907                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
2908                    let abs_path = op
2909                        .uri
2910                        .to_file_path()
2911                        .map_err(|_| anyhow!("can't convert URI to path"))?;
2912                    let options = op.options.map(Into::into).unwrap_or_default();
2913                    if abs_path.ends_with("/") {
2914                        fs.remove_dir(&abs_path, options).await?;
2915                    } else {
2916                        fs.remove_file(&abs_path, options).await?;
2917                    }
2918                }
2919                lsp::DocumentChangeOperation::Edit(op) => {
2920                    let buffer_to_edit = this
2921                        .update(cx, |this, cx| {
2922                            this.open_local_buffer_via_lsp(
2923                                op.text_document.uri,
2924                                lsp_adapter.clone(),
2925                                language_server.clone(),
2926                                cx,
2927                            )
2928                        })
2929                        .await?;
2930
2931                    let edits = this
2932                        .update(cx, |this, cx| {
2933                            let edits = op.edits.into_iter().map(|edit| match edit {
2934                                lsp::OneOf::Left(edit) => edit,
2935                                lsp::OneOf::Right(edit) => edit.text_edit,
2936                            });
2937                            this.edits_from_lsp(
2938                                &buffer_to_edit,
2939                                edits,
2940                                op.text_document.version,
2941                                cx,
2942                            )
2943                        })
2944                        .await?;
2945
2946                    let transaction = buffer_to_edit.update(cx, |buffer, cx| {
2947                        buffer.finalize_last_transaction();
2948                        buffer.start_transaction();
2949                        for (range, text) in edits {
2950                            buffer.edit([range], text, cx);
2951                        }
2952                        let transaction = if buffer.end_transaction(cx).is_some() {
2953                            let transaction = buffer.finalize_last_transaction().unwrap().clone();
2954                            if !push_to_history {
2955                                buffer.forget_transaction(transaction.id);
2956                            }
2957                            Some(transaction)
2958                        } else {
2959                            None
2960                        };
2961
2962                        transaction
2963                    });
2964                    if let Some(transaction) = transaction {
2965                        project_transaction.0.insert(buffer_to_edit, transaction);
2966                    }
2967                }
2968            }
2969        }
2970
2971        Ok(project_transaction)
2972    }
2973
2974    pub fn prepare_rename<T: ToPointUtf16>(
2975        &self,
2976        buffer: ModelHandle<Buffer>,
2977        position: T,
2978        cx: &mut ModelContext<Self>,
2979    ) -> Task<Result<Option<Range<Anchor>>>> {
2980        let position = position.to_point_utf16(buffer.read(cx));
2981        self.request_lsp(buffer, PrepareRename { position }, cx)
2982    }
2983
2984    pub fn perform_rename<T: ToPointUtf16>(
2985        &self,
2986        buffer: ModelHandle<Buffer>,
2987        position: T,
2988        new_name: String,
2989        push_to_history: bool,
2990        cx: &mut ModelContext<Self>,
2991    ) -> Task<Result<ProjectTransaction>> {
2992        let position = position.to_point_utf16(buffer.read(cx));
2993        self.request_lsp(
2994            buffer,
2995            PerformRename {
2996                position,
2997                new_name,
2998                push_to_history,
2999            },
3000            cx,
3001        )
3002    }
3003
3004    pub fn search(
3005        &self,
3006        query: SearchQuery,
3007        cx: &mut ModelContext<Self>,
3008    ) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<Range<Anchor>>>>> {
3009        if self.is_local() {
3010            let snapshots = self
3011                .visible_worktrees(cx)
3012                .filter_map(|tree| {
3013                    let tree = tree.read(cx).as_local()?;
3014                    Some(tree.snapshot())
3015                })
3016                .collect::<Vec<_>>();
3017
3018            let background = cx.background().clone();
3019            let path_count: usize = snapshots.iter().map(|s| s.visible_file_count()).sum();
3020            if path_count == 0 {
3021                return Task::ready(Ok(Default::default()));
3022            }
3023            let workers = background.num_cpus().min(path_count);
3024            let (matching_paths_tx, mut matching_paths_rx) = smol::channel::bounded(1024);
3025            cx.background()
3026                .spawn({
3027                    let fs = self.fs.clone();
3028                    let background = cx.background().clone();
3029                    let query = query.clone();
3030                    async move {
3031                        let fs = &fs;
3032                        let query = &query;
3033                        let matching_paths_tx = &matching_paths_tx;
3034                        let paths_per_worker = (path_count + workers - 1) / workers;
3035                        let snapshots = &snapshots;
3036                        background
3037                            .scoped(|scope| {
3038                                for worker_ix in 0..workers {
3039                                    let worker_start_ix = worker_ix * paths_per_worker;
3040                                    let worker_end_ix = worker_start_ix + paths_per_worker;
3041                                    scope.spawn(async move {
3042                                        let mut snapshot_start_ix = 0;
3043                                        let mut abs_path = PathBuf::new();
3044                                        for snapshot in snapshots {
3045                                            let snapshot_end_ix =
3046                                                snapshot_start_ix + snapshot.visible_file_count();
3047                                            if worker_end_ix <= snapshot_start_ix {
3048                                                break;
3049                                            } else if worker_start_ix > snapshot_end_ix {
3050                                                snapshot_start_ix = snapshot_end_ix;
3051                                                continue;
3052                                            } else {
3053                                                let start_in_snapshot = worker_start_ix
3054                                                    .saturating_sub(snapshot_start_ix);
3055                                                let end_in_snapshot =
3056                                                    cmp::min(worker_end_ix, snapshot_end_ix)
3057                                                        - snapshot_start_ix;
3058
3059                                                for entry in snapshot
3060                                                    .files(false, start_in_snapshot)
3061                                                    .take(end_in_snapshot - start_in_snapshot)
3062                                                {
3063                                                    if matching_paths_tx.is_closed() {
3064                                                        break;
3065                                                    }
3066
3067                                                    abs_path.clear();
3068                                                    abs_path.push(&snapshot.abs_path());
3069                                                    abs_path.push(&entry.path);
3070                                                    let matches = if let Some(file) =
3071                                                        fs.open_sync(&abs_path).await.log_err()
3072                                                    {
3073                                                        query.detect(file).unwrap_or(false)
3074                                                    } else {
3075                                                        false
3076                                                    };
3077
3078                                                    if matches {
3079                                                        let project_path =
3080                                                            (snapshot.id(), entry.path.clone());
3081                                                        if matching_paths_tx
3082                                                            .send(project_path)
3083                                                            .await
3084                                                            .is_err()
3085                                                        {
3086                                                            break;
3087                                                        }
3088                                                    }
3089                                                }
3090
3091                                                snapshot_start_ix = snapshot_end_ix;
3092                                            }
3093                                        }
3094                                    });
3095                                }
3096                            })
3097                            .await;
3098                    }
3099                })
3100                .detach();
3101
3102            let (buffers_tx, buffers_rx) = smol::channel::bounded(1024);
3103            let open_buffers = self
3104                .opened_buffers
3105                .values()
3106                .filter_map(|b| b.upgrade(cx))
3107                .collect::<HashSet<_>>();
3108            cx.spawn(|this, cx| async move {
3109                for buffer in &open_buffers {
3110                    let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
3111                    buffers_tx.send((buffer.clone(), snapshot)).await?;
3112                }
3113
3114                let open_buffers = Rc::new(RefCell::new(open_buffers));
3115                while let Some(project_path) = matching_paths_rx.next().await {
3116                    if buffers_tx.is_closed() {
3117                        break;
3118                    }
3119
3120                    let this = this.clone();
3121                    let open_buffers = open_buffers.clone();
3122                    let buffers_tx = buffers_tx.clone();
3123                    cx.spawn(|mut cx| async move {
3124                        if let Some(buffer) = this
3125                            .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
3126                            .await
3127                            .log_err()
3128                        {
3129                            if open_buffers.borrow_mut().insert(buffer.clone()) {
3130                                let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
3131                                buffers_tx.send((buffer, snapshot)).await?;
3132                            }
3133                        }
3134
3135                        Ok::<_, anyhow::Error>(())
3136                    })
3137                    .detach();
3138                }
3139
3140                Ok::<_, anyhow::Error>(())
3141            })
3142            .detach_and_log_err(cx);
3143
3144            let background = cx.background().clone();
3145            cx.background().spawn(async move {
3146                let query = &query;
3147                let mut matched_buffers = Vec::new();
3148                for _ in 0..workers {
3149                    matched_buffers.push(HashMap::default());
3150                }
3151                background
3152                    .scoped(|scope| {
3153                        for worker_matched_buffers in matched_buffers.iter_mut() {
3154                            let mut buffers_rx = buffers_rx.clone();
3155                            scope.spawn(async move {
3156                                while let Some((buffer, snapshot)) = buffers_rx.next().await {
3157                                    let buffer_matches = query
3158                                        .search(snapshot.as_rope())
3159                                        .await
3160                                        .iter()
3161                                        .map(|range| {
3162                                            snapshot.anchor_before(range.start)
3163                                                ..snapshot.anchor_after(range.end)
3164                                        })
3165                                        .collect::<Vec<_>>();
3166                                    if !buffer_matches.is_empty() {
3167                                        worker_matched_buffers
3168                                            .insert(buffer.clone(), buffer_matches);
3169                                    }
3170                                }
3171                            });
3172                        }
3173                    })
3174                    .await;
3175                Ok(matched_buffers.into_iter().flatten().collect())
3176            })
3177        } else if let Some(project_id) = self.remote_id() {
3178            let request = self.client.request(query.to_proto(project_id));
3179            cx.spawn(|this, mut cx| async move {
3180                let response = request.await?;
3181                let mut result = HashMap::default();
3182                for location in response.locations {
3183                    let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
3184                    let target_buffer = this
3185                        .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
3186                        .await?;
3187                    let start = location
3188                        .start
3189                        .and_then(deserialize_anchor)
3190                        .ok_or_else(|| anyhow!("missing target start"))?;
3191                    let end = location
3192                        .end
3193                        .and_then(deserialize_anchor)
3194                        .ok_or_else(|| anyhow!("missing target end"))?;
3195                    result
3196                        .entry(target_buffer)
3197                        .or_insert(Vec::new())
3198                        .push(start..end)
3199                }
3200                Ok(result)
3201            })
3202        } else {
3203            Task::ready(Ok(Default::default()))
3204        }
3205    }
3206
3207    fn request_lsp<R: LspCommand>(
3208        &self,
3209        buffer_handle: ModelHandle<Buffer>,
3210        request: R,
3211        cx: &mut ModelContext<Self>,
3212    ) -> Task<Result<R::Response>>
3213    where
3214        <R::LspRequest as lsp::request::Request>::Result: Send,
3215    {
3216        let buffer = buffer_handle.read(cx);
3217        if self.is_local() {
3218            let file = File::from_dyn(buffer.file()).and_then(File::as_local);
3219            if let Some((file, (_, language_server))) =
3220                file.zip(self.language_server_for_buffer(buffer, cx).cloned())
3221            {
3222                let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
3223                return cx.spawn(|this, cx| async move {
3224                    if !request.check_capabilities(&language_server.capabilities()) {
3225                        return Ok(Default::default());
3226                    }
3227
3228                    let response = language_server
3229                        .request::<R::LspRequest>(lsp_params)
3230                        .await
3231                        .context("lsp request failed")?;
3232                    request
3233                        .response_from_lsp(response, this, buffer_handle, cx)
3234                        .await
3235                });
3236            }
3237        } else if let Some(project_id) = self.remote_id() {
3238            let rpc = self.client.clone();
3239            let message = request.to_proto(project_id, buffer);
3240            return cx.spawn(|this, cx| async move {
3241                let response = rpc.request(message).await?;
3242                request
3243                    .response_from_proto(response, this, buffer_handle, cx)
3244                    .await
3245            });
3246        }
3247        Task::ready(Ok(Default::default()))
3248    }
3249
3250    pub fn find_or_create_local_worktree(
3251        &mut self,
3252        abs_path: impl AsRef<Path>,
3253        visible: bool,
3254        cx: &mut ModelContext<Self>,
3255    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
3256        let abs_path = abs_path.as_ref();
3257        if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
3258            Task::ready(Ok((tree.clone(), relative_path.into())))
3259        } else {
3260            let worktree = self.create_local_worktree(abs_path, visible, cx);
3261            cx.foreground()
3262                .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
3263        }
3264    }
3265
3266    pub fn find_local_worktree(
3267        &self,
3268        abs_path: &Path,
3269        cx: &AppContext,
3270    ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
3271        for tree in self.worktrees(cx) {
3272            if let Some(relative_path) = tree
3273                .read(cx)
3274                .as_local()
3275                .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
3276            {
3277                return Some((tree.clone(), relative_path.into()));
3278            }
3279        }
3280        None
3281    }
3282
3283    pub fn is_shared(&self) -> bool {
3284        match &self.client_state {
3285            ProjectClientState::Local { is_shared, .. } => *is_shared,
3286            ProjectClientState::Remote { .. } => false,
3287        }
3288    }
3289
3290    fn create_local_worktree(
3291        &mut self,
3292        abs_path: impl AsRef<Path>,
3293        visible: bool,
3294        cx: &mut ModelContext<Self>,
3295    ) -> Task<Result<ModelHandle<Worktree>>> {
3296        let fs = self.fs.clone();
3297        let client = self.client.clone();
3298        let next_entry_id = self.next_entry_id.clone();
3299        let path: Arc<Path> = abs_path.as_ref().into();
3300        let task = self
3301            .loading_local_worktrees
3302            .entry(path.clone())
3303            .or_insert_with(|| {
3304                cx.spawn(|project, mut cx| {
3305                    async move {
3306                        let worktree = Worktree::local(
3307                            client.clone(),
3308                            path.clone(),
3309                            visible,
3310                            fs,
3311                            next_entry_id,
3312                            &mut cx,
3313                        )
3314                        .await;
3315                        project.update(&mut cx, |project, _| {
3316                            project.loading_local_worktrees.remove(&path);
3317                        });
3318                        let worktree = worktree?;
3319
3320                        let (remote_project_id, is_shared) =
3321                            project.update(&mut cx, |project, cx| {
3322                                project.add_worktree(&worktree, cx);
3323                                (project.remote_id(), project.is_shared())
3324                            });
3325
3326                        if let Some(project_id) = remote_project_id {
3327                            if is_shared {
3328                                worktree
3329                                    .update(&mut cx, |worktree, cx| {
3330                                        worktree.as_local_mut().unwrap().share(project_id, cx)
3331                                    })
3332                                    .await?;
3333                            } else {
3334                                worktree
3335                                    .update(&mut cx, |worktree, cx| {
3336                                        worktree.as_local_mut().unwrap().register(project_id, cx)
3337                                    })
3338                                    .await?;
3339                            }
3340                        }
3341
3342                        Ok(worktree)
3343                    }
3344                    .map_err(|err| Arc::new(err))
3345                })
3346                .shared()
3347            })
3348            .clone();
3349        cx.foreground().spawn(async move {
3350            match task.await {
3351                Ok(worktree) => Ok(worktree),
3352                Err(err) => Err(anyhow!("{}", err)),
3353            }
3354        })
3355    }
3356
3357    pub fn remove_worktree(&mut self, id: WorktreeId, cx: &mut ModelContext<Self>) {
3358        self.worktrees.retain(|worktree| {
3359            worktree
3360                .upgrade(cx)
3361                .map_or(false, |w| w.read(cx).id() != id)
3362        });
3363        cx.notify();
3364    }
3365
3366    fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
3367        cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
3368        if worktree.read(cx).is_local() {
3369            cx.subscribe(&worktree, |this, worktree, _, cx| {
3370                this.update_local_worktree_buffers(worktree, cx);
3371            })
3372            .detach();
3373        }
3374
3375        let push_strong_handle = {
3376            let worktree = worktree.read(cx);
3377            self.is_shared() || worktree.is_visible() || worktree.is_remote()
3378        };
3379        if push_strong_handle {
3380            self.worktrees
3381                .push(WorktreeHandle::Strong(worktree.clone()));
3382        } else {
3383            cx.observe_release(&worktree, |this, _, cx| {
3384                this.worktrees
3385                    .retain(|worktree| worktree.upgrade(cx).is_some());
3386                cx.notify();
3387            })
3388            .detach();
3389            self.worktrees
3390                .push(WorktreeHandle::Weak(worktree.downgrade()));
3391        }
3392        cx.notify();
3393    }
3394
3395    fn update_local_worktree_buffers(
3396        &mut self,
3397        worktree_handle: ModelHandle<Worktree>,
3398        cx: &mut ModelContext<Self>,
3399    ) {
3400        let snapshot = worktree_handle.read(cx).snapshot();
3401        let mut buffers_to_delete = Vec::new();
3402        let mut renamed_buffers = Vec::new();
3403        for (buffer_id, buffer) in &self.opened_buffers {
3404            if let Some(buffer) = buffer.upgrade(cx) {
3405                buffer.update(cx, |buffer, cx| {
3406                    if let Some(old_file) = File::from_dyn(buffer.file()) {
3407                        if old_file.worktree != worktree_handle {
3408                            return;
3409                        }
3410
3411                        let new_file = if let Some(entry) = old_file
3412                            .entry_id
3413                            .and_then(|entry_id| snapshot.entry_for_id(entry_id))
3414                        {
3415                            File {
3416                                is_local: true,
3417                                entry_id: Some(entry.id),
3418                                mtime: entry.mtime,
3419                                path: entry.path.clone(),
3420                                worktree: worktree_handle.clone(),
3421                            }
3422                        } else if let Some(entry) =
3423                            snapshot.entry_for_path(old_file.path().as_ref())
3424                        {
3425                            File {
3426                                is_local: true,
3427                                entry_id: Some(entry.id),
3428                                mtime: entry.mtime,
3429                                path: entry.path.clone(),
3430                                worktree: worktree_handle.clone(),
3431                            }
3432                        } else {
3433                            File {
3434                                is_local: true,
3435                                entry_id: None,
3436                                path: old_file.path().clone(),
3437                                mtime: old_file.mtime(),
3438                                worktree: worktree_handle.clone(),
3439                            }
3440                        };
3441
3442                        let old_path = old_file.abs_path(cx);
3443                        if new_file.abs_path(cx) != old_path {
3444                            renamed_buffers.push((cx.handle(), old_path));
3445                        }
3446
3447                        if let Some(project_id) = self.remote_id() {
3448                            self.client
3449                                .send(proto::UpdateBufferFile {
3450                                    project_id,
3451                                    buffer_id: *buffer_id as u64,
3452                                    file: Some(new_file.to_proto()),
3453                                })
3454                                .log_err();
3455                        }
3456                        buffer.file_updated(Box::new(new_file), cx).detach();
3457                    }
3458                });
3459            } else {
3460                buffers_to_delete.push(*buffer_id);
3461            }
3462        }
3463
3464        for buffer_id in buffers_to_delete {
3465            self.opened_buffers.remove(&buffer_id);
3466        }
3467
3468        for (buffer, old_path) in renamed_buffers {
3469            self.unregister_buffer_from_language_server(&buffer, old_path, cx);
3470            self.assign_language_to_buffer(&buffer, cx);
3471            self.register_buffer_with_language_server(&buffer, cx);
3472        }
3473    }
3474
3475    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
3476        let new_active_entry = entry.and_then(|project_path| {
3477            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
3478            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
3479            Some(entry.id)
3480        });
3481        if new_active_entry != self.active_entry {
3482            self.active_entry = new_active_entry;
3483            cx.emit(Event::ActiveEntryChanged(new_active_entry));
3484        }
3485    }
3486
3487    pub fn is_running_disk_based_diagnostics(&self) -> bool {
3488        self.language_server_statuses
3489            .values()
3490            .any(|status| status.pending_diagnostic_updates > 0)
3491    }
3492
3493    pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
3494        let mut summary = DiagnosticSummary::default();
3495        for (_, path_summary) in self.diagnostic_summaries(cx) {
3496            summary.error_count += path_summary.error_count;
3497            summary.warning_count += path_summary.warning_count;
3498        }
3499        summary
3500    }
3501
3502    pub fn diagnostic_summaries<'a>(
3503        &'a self,
3504        cx: &'a AppContext,
3505    ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
3506        self.worktrees(cx).flat_map(move |worktree| {
3507            let worktree = worktree.read(cx);
3508            let worktree_id = worktree.id();
3509            worktree
3510                .diagnostic_summaries()
3511                .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
3512        })
3513    }
3514
3515    pub fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
3516        if self
3517            .language_server_statuses
3518            .values()
3519            .map(|status| status.pending_diagnostic_updates)
3520            .sum::<isize>()
3521            == 1
3522        {
3523            cx.emit(Event::DiskBasedDiagnosticsStarted);
3524        }
3525    }
3526
3527    pub fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
3528        cx.emit(Event::DiskBasedDiagnosticsUpdated);
3529        if self
3530            .language_server_statuses
3531            .values()
3532            .map(|status| status.pending_diagnostic_updates)
3533            .sum::<isize>()
3534            == 0
3535        {
3536            cx.emit(Event::DiskBasedDiagnosticsFinished);
3537        }
3538    }
3539
3540    pub fn active_entry(&self) -> Option<ProjectEntryId> {
3541        self.active_entry
3542    }
3543
3544    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<ProjectEntryId> {
3545        self.worktree_for_id(path.worktree_id, cx)?
3546            .read(cx)
3547            .entry_for_path(&path.path)
3548            .map(|entry| entry.id)
3549    }
3550
3551    pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
3552        let worktree = self.worktree_for_entry(entry_id, cx)?;
3553        let worktree = worktree.read(cx);
3554        let worktree_id = worktree.id();
3555        let path = worktree.entry_for_id(entry_id)?.path.clone();
3556        Some(ProjectPath { worktree_id, path })
3557    }
3558
3559    // RPC message handlers
3560
3561    async fn handle_unshare_project(
3562        this: ModelHandle<Self>,
3563        _: TypedEnvelope<proto::UnshareProject>,
3564        _: Arc<Client>,
3565        mut cx: AsyncAppContext,
3566    ) -> Result<()> {
3567        this.update(&mut cx, |this, cx| this.project_unshared(cx));
3568        Ok(())
3569    }
3570
3571    async fn handle_add_collaborator(
3572        this: ModelHandle<Self>,
3573        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
3574        _: Arc<Client>,
3575        mut cx: AsyncAppContext,
3576    ) -> Result<()> {
3577        let user_store = this.read_with(&cx, |this, _| this.user_store.clone());
3578        let collaborator = envelope
3579            .payload
3580            .collaborator
3581            .take()
3582            .ok_or_else(|| anyhow!("empty collaborator"))?;
3583
3584        let collaborator = Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
3585        this.update(&mut cx, |this, cx| {
3586            this.collaborators
3587                .insert(collaborator.peer_id, collaborator);
3588            cx.notify();
3589        });
3590
3591        Ok(())
3592    }
3593
3594    async fn handle_remove_collaborator(
3595        this: ModelHandle<Self>,
3596        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
3597        _: Arc<Client>,
3598        mut cx: AsyncAppContext,
3599    ) -> Result<()> {
3600        this.update(&mut cx, |this, cx| {
3601            let peer_id = PeerId(envelope.payload.peer_id);
3602            let replica_id = this
3603                .collaborators
3604                .remove(&peer_id)
3605                .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
3606                .replica_id;
3607            for (_, buffer) in &this.opened_buffers {
3608                if let Some(buffer) = buffer.upgrade(cx) {
3609                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
3610                }
3611            }
3612            cx.emit(Event::CollaboratorLeft(peer_id));
3613            cx.notify();
3614            Ok(())
3615        })
3616    }
3617
3618    async fn handle_register_worktree(
3619        this: ModelHandle<Self>,
3620        envelope: TypedEnvelope<proto::RegisterWorktree>,
3621        client: Arc<Client>,
3622        mut cx: AsyncAppContext,
3623    ) -> Result<()> {
3624        this.update(&mut cx, |this, cx| {
3625            let remote_id = this.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
3626            let replica_id = this.replica_id();
3627            let worktree = proto::Worktree {
3628                id: envelope.payload.worktree_id,
3629                root_name: envelope.payload.root_name,
3630                entries: Default::default(),
3631                diagnostic_summaries: Default::default(),
3632                visible: envelope.payload.visible,
3633            };
3634            let (worktree, load_task) =
3635                Worktree::remote(remote_id, replica_id, worktree, client, cx);
3636            this.add_worktree(&worktree, cx);
3637            load_task.detach();
3638            Ok(())
3639        })
3640    }
3641
3642    async fn handle_unregister_worktree(
3643        this: ModelHandle<Self>,
3644        envelope: TypedEnvelope<proto::UnregisterWorktree>,
3645        _: Arc<Client>,
3646        mut cx: AsyncAppContext,
3647    ) -> Result<()> {
3648        this.update(&mut cx, |this, cx| {
3649            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3650            this.remove_worktree(worktree_id, cx);
3651            Ok(())
3652        })
3653    }
3654
3655    async fn handle_update_worktree(
3656        this: ModelHandle<Self>,
3657        envelope: TypedEnvelope<proto::UpdateWorktree>,
3658        _: Arc<Client>,
3659        mut cx: AsyncAppContext,
3660    ) -> Result<()> {
3661        this.update(&mut cx, |this, cx| {
3662            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3663            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
3664                worktree.update(cx, |worktree, _| {
3665                    let worktree = worktree.as_remote_mut().unwrap();
3666                    worktree.update_from_remote(envelope)
3667                })?;
3668            }
3669            Ok(())
3670        })
3671    }
3672
3673    async fn handle_update_diagnostic_summary(
3674        this: ModelHandle<Self>,
3675        envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
3676        _: Arc<Client>,
3677        mut cx: AsyncAppContext,
3678    ) -> Result<()> {
3679        this.update(&mut cx, |this, cx| {
3680            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3681            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
3682                if let Some(summary) = envelope.payload.summary {
3683                    let project_path = ProjectPath {
3684                        worktree_id,
3685                        path: Path::new(&summary.path).into(),
3686                    };
3687                    worktree.update(cx, |worktree, _| {
3688                        worktree
3689                            .as_remote_mut()
3690                            .unwrap()
3691                            .update_diagnostic_summary(project_path.path.clone(), &summary);
3692                    });
3693                    cx.emit(Event::DiagnosticsUpdated(project_path));
3694                }
3695            }
3696            Ok(())
3697        })
3698    }
3699
3700    async fn handle_start_language_server(
3701        this: ModelHandle<Self>,
3702        envelope: TypedEnvelope<proto::StartLanguageServer>,
3703        _: Arc<Client>,
3704        mut cx: AsyncAppContext,
3705    ) -> Result<()> {
3706        let server = envelope
3707            .payload
3708            .server
3709            .ok_or_else(|| anyhow!("invalid server"))?;
3710        this.update(&mut cx, |this, cx| {
3711            this.language_server_statuses.insert(
3712                server.id as usize,
3713                LanguageServerStatus {
3714                    name: server.name,
3715                    pending_work: Default::default(),
3716                    pending_diagnostic_updates: 0,
3717                },
3718            );
3719            cx.notify();
3720        });
3721        Ok(())
3722    }
3723
3724    async fn handle_update_language_server(
3725        this: ModelHandle<Self>,
3726        envelope: TypedEnvelope<proto::UpdateLanguageServer>,
3727        _: Arc<Client>,
3728        mut cx: AsyncAppContext,
3729    ) -> Result<()> {
3730        let language_server_id = envelope.payload.language_server_id as usize;
3731        match envelope
3732            .payload
3733            .variant
3734            .ok_or_else(|| anyhow!("invalid variant"))?
3735        {
3736            proto::update_language_server::Variant::WorkStart(payload) => {
3737                this.update(&mut cx, |this, cx| {
3738                    this.on_lsp_work_start(language_server_id, payload.token, cx);
3739                })
3740            }
3741            proto::update_language_server::Variant::WorkProgress(payload) => {
3742                this.update(&mut cx, |this, cx| {
3743                    this.on_lsp_work_progress(
3744                        language_server_id,
3745                        payload.token,
3746                        LanguageServerProgress {
3747                            message: payload.message,
3748                            percentage: payload.percentage.map(|p| p as usize),
3749                            last_update_at: Instant::now(),
3750                        },
3751                        cx,
3752                    );
3753                })
3754            }
3755            proto::update_language_server::Variant::WorkEnd(payload) => {
3756                this.update(&mut cx, |this, cx| {
3757                    this.on_lsp_work_end(language_server_id, payload.token, cx);
3758                })
3759            }
3760            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
3761                this.update(&mut cx, |this, cx| {
3762                    this.disk_based_diagnostics_started(cx);
3763                })
3764            }
3765            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
3766                this.update(&mut cx, |this, cx| this.disk_based_diagnostics_finished(cx));
3767            }
3768        }
3769
3770        Ok(())
3771    }
3772
3773    async fn handle_update_buffer(
3774        this: ModelHandle<Self>,
3775        envelope: TypedEnvelope<proto::UpdateBuffer>,
3776        _: Arc<Client>,
3777        mut cx: AsyncAppContext,
3778    ) -> Result<()> {
3779        this.update(&mut cx, |this, cx| {
3780            let payload = envelope.payload.clone();
3781            let buffer_id = payload.buffer_id;
3782            let ops = payload
3783                .operations
3784                .into_iter()
3785                .map(|op| language::proto::deserialize_operation(op))
3786                .collect::<Result<Vec<_>, _>>()?;
3787            match this.opened_buffers.entry(buffer_id) {
3788                hash_map::Entry::Occupied(mut e) => match e.get_mut() {
3789                    OpenBuffer::Strong(buffer) => {
3790                        buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
3791                    }
3792                    OpenBuffer::Loading(operations) => operations.extend_from_slice(&ops),
3793                    OpenBuffer::Weak(_) => {}
3794                },
3795                hash_map::Entry::Vacant(e) => {
3796                    e.insert(OpenBuffer::Loading(ops));
3797                }
3798            }
3799            Ok(())
3800        })
3801    }
3802
3803    async fn handle_update_buffer_file(
3804        this: ModelHandle<Self>,
3805        envelope: TypedEnvelope<proto::UpdateBufferFile>,
3806        _: Arc<Client>,
3807        mut cx: AsyncAppContext,
3808    ) -> Result<()> {
3809        this.update(&mut cx, |this, cx| {
3810            let payload = envelope.payload.clone();
3811            let buffer_id = payload.buffer_id;
3812            let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
3813            let worktree = this
3814                .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
3815                .ok_or_else(|| anyhow!("no such worktree"))?;
3816            let file = File::from_proto(file, worktree.clone(), cx)?;
3817            let buffer = this
3818                .opened_buffers
3819                .get_mut(&buffer_id)
3820                .and_then(|b| b.upgrade(cx))
3821                .ok_or_else(|| anyhow!("no such buffer"))?;
3822            buffer.update(cx, |buffer, cx| {
3823                buffer.file_updated(Box::new(file), cx).detach();
3824            });
3825            Ok(())
3826        })
3827    }
3828
3829    async fn handle_save_buffer(
3830        this: ModelHandle<Self>,
3831        envelope: TypedEnvelope<proto::SaveBuffer>,
3832        _: Arc<Client>,
3833        mut cx: AsyncAppContext,
3834    ) -> Result<proto::BufferSaved> {
3835        let buffer_id = envelope.payload.buffer_id;
3836        let requested_version = deserialize_version(envelope.payload.version);
3837
3838        let (project_id, buffer) = this.update(&mut cx, |this, cx| {
3839            let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
3840            let buffer = this
3841                .opened_buffers
3842                .get(&buffer_id)
3843                .and_then(|buffer| buffer.upgrade(cx))
3844                .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
3845            Ok::<_, anyhow::Error>((project_id, buffer))
3846        })?;
3847        buffer
3848            .update(&mut cx, |buffer, _| {
3849                buffer.wait_for_version(requested_version)
3850            })
3851            .await;
3852
3853        let (saved_version, mtime) = buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?;
3854        Ok(proto::BufferSaved {
3855            project_id,
3856            buffer_id,
3857            version: serialize_version(&saved_version),
3858            mtime: Some(mtime.into()),
3859        })
3860    }
3861
3862    async fn handle_reload_buffers(
3863        this: ModelHandle<Self>,
3864        envelope: TypedEnvelope<proto::ReloadBuffers>,
3865        _: Arc<Client>,
3866        mut cx: AsyncAppContext,
3867    ) -> Result<proto::ReloadBuffersResponse> {
3868        let sender_id = envelope.original_sender_id()?;
3869        let reload = this.update(&mut cx, |this, cx| {
3870            let mut buffers = HashSet::default();
3871            for buffer_id in &envelope.payload.buffer_ids {
3872                buffers.insert(
3873                    this.opened_buffers
3874                        .get(buffer_id)
3875                        .and_then(|buffer| buffer.upgrade(cx))
3876                        .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
3877                );
3878            }
3879            Ok::<_, anyhow::Error>(this.reload_buffers(buffers, false, cx))
3880        })?;
3881
3882        let project_transaction = reload.await?;
3883        let project_transaction = this.update(&mut cx, |this, cx| {
3884            this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
3885        });
3886        Ok(proto::ReloadBuffersResponse {
3887            transaction: Some(project_transaction),
3888        })
3889    }
3890
3891    async fn handle_format_buffers(
3892        this: ModelHandle<Self>,
3893        envelope: TypedEnvelope<proto::FormatBuffers>,
3894        _: Arc<Client>,
3895        mut cx: AsyncAppContext,
3896    ) -> Result<proto::FormatBuffersResponse> {
3897        let sender_id = envelope.original_sender_id()?;
3898        let format = this.update(&mut cx, |this, cx| {
3899            let mut buffers = HashSet::default();
3900            for buffer_id in &envelope.payload.buffer_ids {
3901                buffers.insert(
3902                    this.opened_buffers
3903                        .get(buffer_id)
3904                        .and_then(|buffer| buffer.upgrade(cx))
3905                        .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
3906                );
3907            }
3908            Ok::<_, anyhow::Error>(this.format(buffers, false, cx))
3909        })?;
3910
3911        let project_transaction = format.await?;
3912        let project_transaction = this.update(&mut cx, |this, cx| {
3913            this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
3914        });
3915        Ok(proto::FormatBuffersResponse {
3916            transaction: Some(project_transaction),
3917        })
3918    }
3919
3920    async fn handle_get_completions(
3921        this: ModelHandle<Self>,
3922        envelope: TypedEnvelope<proto::GetCompletions>,
3923        _: Arc<Client>,
3924        mut cx: AsyncAppContext,
3925    ) -> Result<proto::GetCompletionsResponse> {
3926        let position = envelope
3927            .payload
3928            .position
3929            .and_then(language::proto::deserialize_anchor)
3930            .ok_or_else(|| anyhow!("invalid position"))?;
3931        let version = deserialize_version(envelope.payload.version);
3932        let buffer = this.read_with(&cx, |this, cx| {
3933            this.opened_buffers
3934                .get(&envelope.payload.buffer_id)
3935                .and_then(|buffer| buffer.upgrade(cx))
3936                .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
3937        })?;
3938        buffer
3939            .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
3940            .await;
3941        let version = buffer.read_with(&cx, |buffer, _| buffer.version());
3942        let completions = this
3943            .update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
3944            .await?;
3945
3946        Ok(proto::GetCompletionsResponse {
3947            completions: completions
3948                .iter()
3949                .map(language::proto::serialize_completion)
3950                .collect(),
3951            version: serialize_version(&version),
3952        })
3953    }
3954
3955    async fn handle_apply_additional_edits_for_completion(
3956        this: ModelHandle<Self>,
3957        envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
3958        _: Arc<Client>,
3959        mut cx: AsyncAppContext,
3960    ) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
3961        let apply_additional_edits = this.update(&mut cx, |this, cx| {
3962            let buffer = this
3963                .opened_buffers
3964                .get(&envelope.payload.buffer_id)
3965                .and_then(|buffer| buffer.upgrade(cx))
3966                .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
3967            let language = buffer.read(cx).language();
3968            let completion = language::proto::deserialize_completion(
3969                envelope
3970                    .payload
3971                    .completion
3972                    .ok_or_else(|| anyhow!("invalid completion"))?,
3973                language,
3974            )?;
3975            Ok::<_, anyhow::Error>(
3976                this.apply_additional_edits_for_completion(buffer, completion, false, cx),
3977            )
3978        })?;
3979
3980        Ok(proto::ApplyCompletionAdditionalEditsResponse {
3981            transaction: apply_additional_edits
3982                .await?
3983                .as_ref()
3984                .map(language::proto::serialize_transaction),
3985        })
3986    }
3987
3988    async fn handle_get_code_actions(
3989        this: ModelHandle<Self>,
3990        envelope: TypedEnvelope<proto::GetCodeActions>,
3991        _: Arc<Client>,
3992        mut cx: AsyncAppContext,
3993    ) -> Result<proto::GetCodeActionsResponse> {
3994        let start = envelope
3995            .payload
3996            .start
3997            .and_then(language::proto::deserialize_anchor)
3998            .ok_or_else(|| anyhow!("invalid start"))?;
3999        let end = envelope
4000            .payload
4001            .end
4002            .and_then(language::proto::deserialize_anchor)
4003            .ok_or_else(|| anyhow!("invalid end"))?;
4004        let buffer = this.update(&mut cx, |this, cx| {
4005            this.opened_buffers
4006                .get(&envelope.payload.buffer_id)
4007                .and_then(|buffer| buffer.upgrade(cx))
4008                .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
4009        })?;
4010        buffer
4011            .update(&mut cx, |buffer, _| {
4012                buffer.wait_for_version(deserialize_version(envelope.payload.version))
4013            })
4014            .await;
4015
4016        let version = buffer.read_with(&cx, |buffer, _| buffer.version());
4017        let code_actions = this.update(&mut cx, |this, cx| {
4018            Ok::<_, anyhow::Error>(this.code_actions(&buffer, start..end, cx))
4019        })?;
4020
4021        Ok(proto::GetCodeActionsResponse {
4022            actions: code_actions
4023                .await?
4024                .iter()
4025                .map(language::proto::serialize_code_action)
4026                .collect(),
4027            version: serialize_version(&version),
4028        })
4029    }
4030
4031    async fn handle_apply_code_action(
4032        this: ModelHandle<Self>,
4033        envelope: TypedEnvelope<proto::ApplyCodeAction>,
4034        _: Arc<Client>,
4035        mut cx: AsyncAppContext,
4036    ) -> Result<proto::ApplyCodeActionResponse> {
4037        let sender_id = envelope.original_sender_id()?;
4038        let action = language::proto::deserialize_code_action(
4039            envelope
4040                .payload
4041                .action
4042                .ok_or_else(|| anyhow!("invalid action"))?,
4043        )?;
4044        let apply_code_action = this.update(&mut cx, |this, cx| {
4045            let buffer = this
4046                .opened_buffers
4047                .get(&envelope.payload.buffer_id)
4048                .and_then(|buffer| buffer.upgrade(cx))
4049                .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
4050            Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
4051        })?;
4052
4053        let project_transaction = apply_code_action.await?;
4054        let project_transaction = this.update(&mut cx, |this, cx| {
4055            this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
4056        });
4057        Ok(proto::ApplyCodeActionResponse {
4058            transaction: Some(project_transaction),
4059        })
4060    }
4061
4062    async fn handle_lsp_command<T: LspCommand>(
4063        this: ModelHandle<Self>,
4064        envelope: TypedEnvelope<T::ProtoRequest>,
4065        _: Arc<Client>,
4066        mut cx: AsyncAppContext,
4067    ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
4068    where
4069        <T::LspRequest as lsp::request::Request>::Result: Send,
4070    {
4071        let sender_id = envelope.original_sender_id()?;
4072        let buffer_id = T::buffer_id_from_proto(&envelope.payload);
4073        let buffer_handle = this.read_with(&cx, |this, _| {
4074            this.opened_buffers
4075                .get(&buffer_id)
4076                .and_then(|buffer| buffer.upgrade(&cx))
4077                .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
4078        })?;
4079        let request = T::from_proto(
4080            envelope.payload,
4081            this.clone(),
4082            buffer_handle.clone(),
4083            cx.clone(),
4084        )
4085        .await?;
4086        let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version());
4087        let response = this
4088            .update(&mut cx, |this, cx| {
4089                this.request_lsp(buffer_handle, request, cx)
4090            })
4091            .await?;
4092        this.update(&mut cx, |this, cx| {
4093            Ok(T::response_to_proto(
4094                response,
4095                this,
4096                sender_id,
4097                &buffer_version,
4098                cx,
4099            ))
4100        })
4101    }
4102
4103    async fn handle_get_project_symbols(
4104        this: ModelHandle<Self>,
4105        envelope: TypedEnvelope<proto::GetProjectSymbols>,
4106        _: Arc<Client>,
4107        mut cx: AsyncAppContext,
4108    ) -> Result<proto::GetProjectSymbolsResponse> {
4109        let symbols = this
4110            .update(&mut cx, |this, cx| {
4111                this.symbols(&envelope.payload.query, cx)
4112            })
4113            .await?;
4114
4115        Ok(proto::GetProjectSymbolsResponse {
4116            symbols: symbols.iter().map(serialize_symbol).collect(),
4117        })
4118    }
4119
4120    async fn handle_search_project(
4121        this: ModelHandle<Self>,
4122        envelope: TypedEnvelope<proto::SearchProject>,
4123        _: Arc<Client>,
4124        mut cx: AsyncAppContext,
4125    ) -> Result<proto::SearchProjectResponse> {
4126        let peer_id = envelope.original_sender_id()?;
4127        let query = SearchQuery::from_proto(envelope.payload)?;
4128        let result = this
4129            .update(&mut cx, |this, cx| this.search(query, cx))
4130            .await?;
4131
4132        this.update(&mut cx, |this, cx| {
4133            let mut locations = Vec::new();
4134            for (buffer, ranges) in result {
4135                for range in ranges {
4136                    let start = serialize_anchor(&range.start);
4137                    let end = serialize_anchor(&range.end);
4138                    let buffer = this.serialize_buffer_for_peer(&buffer, peer_id, cx);
4139                    locations.push(proto::Location {
4140                        buffer: Some(buffer),
4141                        start: Some(start),
4142                        end: Some(end),
4143                    });
4144                }
4145            }
4146            Ok(proto::SearchProjectResponse { locations })
4147        })
4148    }
4149
4150    async fn handle_open_buffer_for_symbol(
4151        this: ModelHandle<Self>,
4152        envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
4153        _: Arc<Client>,
4154        mut cx: AsyncAppContext,
4155    ) -> Result<proto::OpenBufferForSymbolResponse> {
4156        let peer_id = envelope.original_sender_id()?;
4157        let symbol = envelope
4158            .payload
4159            .symbol
4160            .ok_or_else(|| anyhow!("invalid symbol"))?;
4161        let symbol = this.read_with(&cx, |this, _| {
4162            let symbol = this.deserialize_symbol(symbol)?;
4163            let signature = this.symbol_signature(symbol.worktree_id, &symbol.path);
4164            if signature == symbol.signature {
4165                Ok(symbol)
4166            } else {
4167                Err(anyhow!("invalid symbol signature"))
4168            }
4169        })?;
4170        let buffer = this
4171            .update(&mut cx, |this, cx| this.open_buffer_for_symbol(&symbol, cx))
4172            .await?;
4173
4174        Ok(proto::OpenBufferForSymbolResponse {
4175            buffer: Some(this.update(&mut cx, |this, cx| {
4176                this.serialize_buffer_for_peer(&buffer, peer_id, cx)
4177            })),
4178        })
4179    }
4180
4181    fn symbol_signature(&self, worktree_id: WorktreeId, path: &Path) -> [u8; 32] {
4182        let mut hasher = Sha256::new();
4183        hasher.update(worktree_id.to_proto().to_be_bytes());
4184        hasher.update(path.to_string_lossy().as_bytes());
4185        hasher.update(self.nonce.to_be_bytes());
4186        hasher.finalize().as_slice().try_into().unwrap()
4187    }
4188
4189    async fn handle_open_buffer_by_id(
4190        this: ModelHandle<Self>,
4191        envelope: TypedEnvelope<proto::OpenBufferById>,
4192        _: Arc<Client>,
4193        mut cx: AsyncAppContext,
4194    ) -> Result<proto::OpenBufferResponse> {
4195        let peer_id = envelope.original_sender_id()?;
4196        let buffer = this
4197            .update(&mut cx, |this, cx| {
4198                this.open_buffer_by_id(envelope.payload.id, cx)
4199            })
4200            .await?;
4201        this.update(&mut cx, |this, cx| {
4202            Ok(proto::OpenBufferResponse {
4203                buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
4204            })
4205        })
4206    }
4207
4208    async fn handle_open_buffer_by_path(
4209        this: ModelHandle<Self>,
4210        envelope: TypedEnvelope<proto::OpenBufferByPath>,
4211        _: Arc<Client>,
4212        mut cx: AsyncAppContext,
4213    ) -> Result<proto::OpenBufferResponse> {
4214        let peer_id = envelope.original_sender_id()?;
4215        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
4216        let open_buffer = this.update(&mut cx, |this, cx| {
4217            this.open_buffer(
4218                ProjectPath {
4219                    worktree_id,
4220                    path: PathBuf::from(envelope.payload.path).into(),
4221                },
4222                cx,
4223            )
4224        });
4225
4226        let buffer = open_buffer.await?;
4227        this.update(&mut cx, |this, cx| {
4228            Ok(proto::OpenBufferResponse {
4229                buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
4230            })
4231        })
4232    }
4233
4234    fn serialize_project_transaction_for_peer(
4235        &mut self,
4236        project_transaction: ProjectTransaction,
4237        peer_id: PeerId,
4238        cx: &AppContext,
4239    ) -> proto::ProjectTransaction {
4240        let mut serialized_transaction = proto::ProjectTransaction {
4241            buffers: Default::default(),
4242            transactions: Default::default(),
4243        };
4244        for (buffer, transaction) in project_transaction.0 {
4245            serialized_transaction
4246                .buffers
4247                .push(self.serialize_buffer_for_peer(&buffer, peer_id, cx));
4248            serialized_transaction
4249                .transactions
4250                .push(language::proto::serialize_transaction(&transaction));
4251        }
4252        serialized_transaction
4253    }
4254
4255    fn deserialize_project_transaction(
4256        &mut self,
4257        message: proto::ProjectTransaction,
4258        push_to_history: bool,
4259        cx: &mut ModelContext<Self>,
4260    ) -> Task<Result<ProjectTransaction>> {
4261        cx.spawn(|this, mut cx| async move {
4262            let mut project_transaction = ProjectTransaction::default();
4263            for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
4264                let buffer = this
4265                    .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
4266                    .await?;
4267                let transaction = language::proto::deserialize_transaction(transaction)?;
4268                project_transaction.0.insert(buffer, transaction);
4269            }
4270
4271            for (buffer, transaction) in &project_transaction.0 {
4272                buffer
4273                    .update(&mut cx, |buffer, _| {
4274                        buffer.wait_for_edits(transaction.edit_ids.iter().copied())
4275                    })
4276                    .await;
4277
4278                if push_to_history {
4279                    buffer.update(&mut cx, |buffer, _| {
4280                        buffer.push_transaction(transaction.clone(), Instant::now());
4281                    });
4282                }
4283            }
4284
4285            Ok(project_transaction)
4286        })
4287    }
4288
4289    fn serialize_buffer_for_peer(
4290        &mut self,
4291        buffer: &ModelHandle<Buffer>,
4292        peer_id: PeerId,
4293        cx: &AppContext,
4294    ) -> proto::Buffer {
4295        let buffer_id = buffer.read(cx).remote_id();
4296        let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
4297        if shared_buffers.insert(buffer_id) {
4298            proto::Buffer {
4299                variant: Some(proto::buffer::Variant::State(buffer.read(cx).to_proto())),
4300            }
4301        } else {
4302            proto::Buffer {
4303                variant: Some(proto::buffer::Variant::Id(buffer_id)),
4304            }
4305        }
4306    }
4307
4308    fn deserialize_buffer(
4309        &mut self,
4310        buffer: proto::Buffer,
4311        cx: &mut ModelContext<Self>,
4312    ) -> Task<Result<ModelHandle<Buffer>>> {
4313        let replica_id = self.replica_id();
4314
4315        let opened_buffer_tx = self.opened_buffer.0.clone();
4316        let mut opened_buffer_rx = self.opened_buffer.1.clone();
4317        cx.spawn(|this, mut cx| async move {
4318            match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
4319                proto::buffer::Variant::Id(id) => {
4320                    let buffer = loop {
4321                        let buffer = this.read_with(&cx, |this, cx| {
4322                            this.opened_buffers
4323                                .get(&id)
4324                                .and_then(|buffer| buffer.upgrade(cx))
4325                        });
4326                        if let Some(buffer) = buffer {
4327                            break buffer;
4328                        }
4329                        opened_buffer_rx
4330                            .next()
4331                            .await
4332                            .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
4333                    };
4334                    Ok(buffer)
4335                }
4336                proto::buffer::Variant::State(mut buffer) => {
4337                    let mut buffer_worktree = None;
4338                    let mut buffer_file = None;
4339                    if let Some(file) = buffer.file.take() {
4340                        this.read_with(&cx, |this, cx| {
4341                            let worktree_id = WorktreeId::from_proto(file.worktree_id);
4342                            let worktree =
4343                                this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
4344                                    anyhow!("no worktree found for id {}", file.worktree_id)
4345                                })?;
4346                            buffer_file =
4347                                Some(Box::new(File::from_proto(file, worktree.clone(), cx)?)
4348                                    as Box<dyn language::File>);
4349                            buffer_worktree = Some(worktree);
4350                            Ok::<_, anyhow::Error>(())
4351                        })?;
4352                    }
4353
4354                    let buffer = cx.add_model(|cx| {
4355                        Buffer::from_proto(replica_id, buffer, buffer_file, cx).unwrap()
4356                    });
4357
4358                    this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
4359
4360                    *opened_buffer_tx.borrow_mut().borrow_mut() = ();
4361                    Ok(buffer)
4362                }
4363            }
4364        })
4365    }
4366
4367    fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result<Symbol> {
4368        let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
4369        let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
4370        let start = serialized_symbol
4371            .start
4372            .ok_or_else(|| anyhow!("invalid start"))?;
4373        let end = serialized_symbol
4374            .end
4375            .ok_or_else(|| anyhow!("invalid end"))?;
4376        let kind = unsafe { mem::transmute(serialized_symbol.kind) };
4377        let path = PathBuf::from(serialized_symbol.path);
4378        let language = self.languages.select_language(&path);
4379        Ok(Symbol {
4380            source_worktree_id,
4381            worktree_id,
4382            language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
4383            label: language
4384                .and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind))
4385                .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)),
4386            name: serialized_symbol.name,
4387            path,
4388            range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column),
4389            kind,
4390            signature: serialized_symbol
4391                .signature
4392                .try_into()
4393                .map_err(|_| anyhow!("invalid signature"))?,
4394        })
4395    }
4396
4397    async fn handle_buffer_saved(
4398        this: ModelHandle<Self>,
4399        envelope: TypedEnvelope<proto::BufferSaved>,
4400        _: Arc<Client>,
4401        mut cx: AsyncAppContext,
4402    ) -> Result<()> {
4403        let version = deserialize_version(envelope.payload.version);
4404        let mtime = envelope
4405            .payload
4406            .mtime
4407            .ok_or_else(|| anyhow!("missing mtime"))?
4408            .into();
4409
4410        this.update(&mut cx, |this, cx| {
4411            let buffer = this
4412                .opened_buffers
4413                .get(&envelope.payload.buffer_id)
4414                .and_then(|buffer| buffer.upgrade(cx));
4415            if let Some(buffer) = buffer {
4416                buffer.update(cx, |buffer, cx| {
4417                    buffer.did_save(version, mtime, None, cx);
4418                });
4419            }
4420            Ok(())
4421        })
4422    }
4423
4424    async fn handle_buffer_reloaded(
4425        this: ModelHandle<Self>,
4426        envelope: TypedEnvelope<proto::BufferReloaded>,
4427        _: Arc<Client>,
4428        mut cx: AsyncAppContext,
4429    ) -> Result<()> {
4430        let payload = envelope.payload.clone();
4431        let version = deserialize_version(payload.version);
4432        let mtime = payload
4433            .mtime
4434            .ok_or_else(|| anyhow!("missing mtime"))?
4435            .into();
4436        this.update(&mut cx, |this, cx| {
4437            let buffer = this
4438                .opened_buffers
4439                .get(&payload.buffer_id)
4440                .and_then(|buffer| buffer.upgrade(cx));
4441            if let Some(buffer) = buffer {
4442                buffer.update(cx, |buffer, cx| {
4443                    buffer.did_reload(version, mtime, cx);
4444                });
4445            }
4446            Ok(())
4447        })
4448    }
4449
4450    pub fn match_paths<'a>(
4451        &self,
4452        query: &'a str,
4453        include_ignored: bool,
4454        smart_case: bool,
4455        max_results: usize,
4456        cancel_flag: &'a AtomicBool,
4457        cx: &AppContext,
4458    ) -> impl 'a + Future<Output = Vec<PathMatch>> {
4459        let worktrees = self
4460            .worktrees(cx)
4461            .filter(|worktree| worktree.read(cx).is_visible())
4462            .collect::<Vec<_>>();
4463        let include_root_name = worktrees.len() > 1;
4464        let candidate_sets = worktrees
4465            .into_iter()
4466            .map(|worktree| CandidateSet {
4467                snapshot: worktree.read(cx).snapshot(),
4468                include_ignored,
4469                include_root_name,
4470            })
4471            .collect::<Vec<_>>();
4472
4473        let background = cx.background().clone();
4474        async move {
4475            fuzzy::match_paths(
4476                candidate_sets.as_slice(),
4477                query,
4478                smart_case,
4479                max_results,
4480                cancel_flag,
4481                background,
4482            )
4483            .await
4484        }
4485    }
4486
4487    fn edits_from_lsp(
4488        &mut self,
4489        buffer: &ModelHandle<Buffer>,
4490        lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
4491        version: Option<i32>,
4492        cx: &mut ModelContext<Self>,
4493    ) -> Task<Result<Vec<(Range<Anchor>, String)>>> {
4494        let snapshot = self.buffer_snapshot_for_lsp_version(buffer, version, cx);
4495        cx.background().spawn(async move {
4496            let snapshot = snapshot?;
4497            let mut lsp_edits = lsp_edits
4498                .into_iter()
4499                .map(|edit| (range_from_lsp(edit.range), edit.new_text))
4500                .peekable();
4501
4502            let mut edits = Vec::new();
4503            while let Some((mut range, mut new_text)) = lsp_edits.next() {
4504                // Combine any LSP edits that are adjacent.
4505                //
4506                // Also, combine LSP edits that are separated from each other by only
4507                // a newline. This is important because for some code actions,
4508                // Rust-analyzer rewrites the entire buffer via a series of edits that
4509                // are separated by unchanged newline characters.
4510                //
4511                // In order for the diffing logic below to work properly, any edits that
4512                // cancel each other out must be combined into one.
4513                while let Some((next_range, next_text)) = lsp_edits.peek() {
4514                    if next_range.start > range.end {
4515                        if next_range.start.row > range.end.row + 1
4516                            || next_range.start.column > 0
4517                            || snapshot.clip_point_utf16(
4518                                PointUtf16::new(range.end.row, u32::MAX),
4519                                Bias::Left,
4520                            ) > range.end
4521                        {
4522                            break;
4523                        }
4524                        new_text.push('\n');
4525                    }
4526                    range.end = next_range.end;
4527                    new_text.push_str(&next_text);
4528                    lsp_edits.next();
4529                }
4530
4531                if snapshot.clip_point_utf16(range.start, Bias::Left) != range.start
4532                    || snapshot.clip_point_utf16(range.end, Bias::Left) != range.end
4533                {
4534                    return Err(anyhow!("invalid edits received from language server"));
4535                }
4536
4537                // For multiline edits, perform a diff of the old and new text so that
4538                // we can identify the changes more precisely, preserving the locations
4539                // of any anchors positioned in the unchanged regions.
4540                if range.end.row > range.start.row {
4541                    let mut offset = range.start.to_offset(&snapshot);
4542                    let old_text = snapshot.text_for_range(range).collect::<String>();
4543
4544                    let diff = TextDiff::from_lines(old_text.as_str(), &new_text);
4545                    let mut moved_since_edit = true;
4546                    for change in diff.iter_all_changes() {
4547                        let tag = change.tag();
4548                        let value = change.value();
4549                        match tag {
4550                            ChangeTag::Equal => {
4551                                offset += value.len();
4552                                moved_since_edit = true;
4553                            }
4554                            ChangeTag::Delete => {
4555                                let start = snapshot.anchor_after(offset);
4556                                let end = snapshot.anchor_before(offset + value.len());
4557                                if moved_since_edit {
4558                                    edits.push((start..end, String::new()));
4559                                } else {
4560                                    edits.last_mut().unwrap().0.end = end;
4561                                }
4562                                offset += value.len();
4563                                moved_since_edit = false;
4564                            }
4565                            ChangeTag::Insert => {
4566                                if moved_since_edit {
4567                                    let anchor = snapshot.anchor_after(offset);
4568                                    edits.push((anchor.clone()..anchor, value.to_string()));
4569                                } else {
4570                                    edits.last_mut().unwrap().1.push_str(value);
4571                                }
4572                                moved_since_edit = false;
4573                            }
4574                        }
4575                    }
4576                } else if range.end == range.start {
4577                    let anchor = snapshot.anchor_after(range.start);
4578                    edits.push((anchor.clone()..anchor, new_text));
4579                } else {
4580                    let edit_start = snapshot.anchor_after(range.start);
4581                    let edit_end = snapshot.anchor_before(range.end);
4582                    edits.push((edit_start..edit_end, new_text));
4583                }
4584            }
4585
4586            Ok(edits)
4587        })
4588    }
4589
4590    fn buffer_snapshot_for_lsp_version(
4591        &mut self,
4592        buffer: &ModelHandle<Buffer>,
4593        version: Option<i32>,
4594        cx: &AppContext,
4595    ) -> Result<TextBufferSnapshot> {
4596        const OLD_VERSIONS_TO_RETAIN: i32 = 10;
4597
4598        if let Some(version) = version {
4599            let buffer_id = buffer.read(cx).remote_id();
4600            let snapshots = self
4601                .buffer_snapshots
4602                .get_mut(&buffer_id)
4603                .ok_or_else(|| anyhow!("no snapshot found for buffer {}", buffer_id))?;
4604            let mut found_snapshot = None;
4605            snapshots.retain(|(snapshot_version, snapshot)| {
4606                if snapshot_version + OLD_VERSIONS_TO_RETAIN < version {
4607                    false
4608                } else {
4609                    if *snapshot_version == version {
4610                        found_snapshot = Some(snapshot.clone());
4611                    }
4612                    true
4613                }
4614            });
4615
4616            found_snapshot.ok_or_else(|| {
4617                anyhow!(
4618                    "snapshot not found for buffer {} at version {}",
4619                    buffer_id,
4620                    version
4621                )
4622            })
4623        } else {
4624            Ok((buffer.read(cx)).text_snapshot())
4625        }
4626    }
4627
4628    fn language_server_for_buffer(
4629        &self,
4630        buffer: &Buffer,
4631        cx: &AppContext,
4632    ) -> Option<&(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
4633        if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
4634            let worktree_id = file.worktree_id(cx);
4635            self.language_servers
4636                .get(&(worktree_id, language.lsp_adapter()?.name()))
4637        } else {
4638            None
4639        }
4640    }
4641}
4642
4643impl WorktreeHandle {
4644    pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
4645        match self {
4646            WorktreeHandle::Strong(handle) => Some(handle.clone()),
4647            WorktreeHandle::Weak(handle) => handle.upgrade(cx),
4648        }
4649    }
4650}
4651
4652impl OpenBuffer {
4653    pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
4654        match self {
4655            OpenBuffer::Strong(handle) => Some(handle.clone()),
4656            OpenBuffer::Weak(handle) => handle.upgrade(cx),
4657            OpenBuffer::Loading(_) => None,
4658        }
4659    }
4660}
4661
4662struct CandidateSet {
4663    snapshot: Snapshot,
4664    include_ignored: bool,
4665    include_root_name: bool,
4666}
4667
4668impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
4669    type Candidates = CandidateSetIter<'a>;
4670
4671    fn id(&self) -> usize {
4672        self.snapshot.id().to_usize()
4673    }
4674
4675    fn len(&self) -> usize {
4676        if self.include_ignored {
4677            self.snapshot.file_count()
4678        } else {
4679            self.snapshot.visible_file_count()
4680        }
4681    }
4682
4683    fn prefix(&self) -> Arc<str> {
4684        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
4685            self.snapshot.root_name().into()
4686        } else if self.include_root_name {
4687            format!("{}/", self.snapshot.root_name()).into()
4688        } else {
4689            "".into()
4690        }
4691    }
4692
4693    fn candidates(&'a self, start: usize) -> Self::Candidates {
4694        CandidateSetIter {
4695            traversal: self.snapshot.files(self.include_ignored, start),
4696        }
4697    }
4698}
4699
4700struct CandidateSetIter<'a> {
4701    traversal: Traversal<'a>,
4702}
4703
4704impl<'a> Iterator for CandidateSetIter<'a> {
4705    type Item = PathMatchCandidate<'a>;
4706
4707    fn next(&mut self) -> Option<Self::Item> {
4708        self.traversal.next().map(|entry| {
4709            if let EntryKind::File(char_bag) = entry.kind {
4710                PathMatchCandidate {
4711                    path: &entry.path,
4712                    char_bag,
4713                }
4714            } else {
4715                unreachable!()
4716            }
4717        })
4718    }
4719}
4720
4721impl Entity for Project {
4722    type Event = Event;
4723
4724    fn release(&mut self, _: &mut gpui::MutableAppContext) {
4725        match &self.client_state {
4726            ProjectClientState::Local { remote_id_rx, .. } => {
4727                if let Some(project_id) = *remote_id_rx.borrow() {
4728                    self.client
4729                        .send(proto::UnregisterProject { project_id })
4730                        .log_err();
4731                }
4732            }
4733            ProjectClientState::Remote { remote_id, .. } => {
4734                self.client
4735                    .send(proto::LeaveProject {
4736                        project_id: *remote_id,
4737                    })
4738                    .log_err();
4739            }
4740        }
4741    }
4742
4743    fn app_will_quit(
4744        &mut self,
4745        _: &mut MutableAppContext,
4746    ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
4747        let shutdown_futures = self
4748            .language_servers
4749            .drain()
4750            .filter_map(|(_, (_, server))| server.shutdown())
4751            .collect::<Vec<_>>();
4752        Some(
4753            async move {
4754                futures::future::join_all(shutdown_futures).await;
4755            }
4756            .boxed(),
4757        )
4758    }
4759}
4760
4761impl Collaborator {
4762    fn from_proto(
4763        message: proto::Collaborator,
4764        user_store: &ModelHandle<UserStore>,
4765        cx: &mut AsyncAppContext,
4766    ) -> impl Future<Output = Result<Self>> {
4767        let user = user_store.update(cx, |user_store, cx| {
4768            user_store.fetch_user(message.user_id, cx)
4769        });
4770
4771        async move {
4772            Ok(Self {
4773                peer_id: PeerId(message.peer_id),
4774                user: user.await?,
4775                replica_id: message.replica_id as ReplicaId,
4776            })
4777        }
4778    }
4779}
4780
4781impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
4782    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
4783        Self {
4784            worktree_id,
4785            path: path.as_ref().into(),
4786        }
4787    }
4788}
4789
4790impl From<lsp::CreateFileOptions> for fs::CreateOptions {
4791    fn from(options: lsp::CreateFileOptions) -> Self {
4792        Self {
4793            overwrite: options.overwrite.unwrap_or(false),
4794            ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
4795        }
4796    }
4797}
4798
4799impl From<lsp::RenameFileOptions> for fs::RenameOptions {
4800    fn from(options: lsp::RenameFileOptions) -> Self {
4801        Self {
4802            overwrite: options.overwrite.unwrap_or(false),
4803            ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
4804        }
4805    }
4806}
4807
4808impl From<lsp::DeleteFileOptions> for fs::RemoveOptions {
4809    fn from(options: lsp::DeleteFileOptions) -> Self {
4810        Self {
4811            recursive: options.recursive.unwrap_or(false),
4812            ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
4813        }
4814    }
4815}
4816
4817fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
4818    proto::Symbol {
4819        source_worktree_id: symbol.source_worktree_id.to_proto(),
4820        worktree_id: symbol.worktree_id.to_proto(),
4821        language_server_name: symbol.language_server_name.0.to_string(),
4822        name: symbol.name.clone(),
4823        kind: unsafe { mem::transmute(symbol.kind) },
4824        path: symbol.path.to_string_lossy().to_string(),
4825        start: Some(proto::Point {
4826            row: symbol.range.start.row,
4827            column: symbol.range.start.column,
4828        }),
4829        end: Some(proto::Point {
4830            row: symbol.range.end.row,
4831            column: symbol.range.end.column,
4832        }),
4833        signature: symbol.signature.to_vec(),
4834    }
4835}
4836
4837fn relativize_path(base: &Path, path: &Path) -> PathBuf {
4838    let mut path_components = path.components();
4839    let mut base_components = base.components();
4840    let mut components: Vec<Component> = Vec::new();
4841    loop {
4842        match (path_components.next(), base_components.next()) {
4843            (None, None) => break,
4844            (Some(a), None) => {
4845                components.push(a);
4846                components.extend(path_components.by_ref());
4847                break;
4848            }
4849            (None, _) => components.push(Component::ParentDir),
4850            (Some(a), Some(b)) if components.is_empty() && a == b => (),
4851            (Some(a), Some(b)) if b == Component::CurDir => components.push(a),
4852            (Some(a), Some(_)) => {
4853                components.push(Component::ParentDir);
4854                for _ in base_components {
4855                    components.push(Component::ParentDir);
4856                }
4857                components.push(a);
4858                components.extend(path_components.by_ref());
4859                break;
4860            }
4861        }
4862    }
4863    components.iter().map(|c| c.as_os_str()).collect()
4864}
4865
4866impl Item for Buffer {
4867    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
4868        File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
4869    }
4870}
4871
4872#[cfg(test)]
4873mod tests {
4874    use super::{Event, *};
4875    use fs::RealFs;
4876    use futures::{future, StreamExt};
4877    use gpui::test::subscribe;
4878    use language::{
4879        tree_sitter_rust, Diagnostic, FakeLspAdapter, LanguageConfig, OffsetRangeExt, Point,
4880        ToPoint,
4881    };
4882    use lsp::Url;
4883    use serde_json::json;
4884    use std::{cell::RefCell, os::unix, path::PathBuf, rc::Rc, task::Poll};
4885    use unindent::Unindent as _;
4886    use util::{assert_set_eq, test::temp_tree};
4887    use worktree::WorktreeHandle as _;
4888
4889    #[gpui::test]
4890    async fn test_populate_and_search(cx: &mut gpui::TestAppContext) {
4891        let dir = temp_tree(json!({
4892            "root": {
4893                "apple": "",
4894                "banana": {
4895                    "carrot": {
4896                        "date": "",
4897                        "endive": "",
4898                    }
4899                },
4900                "fennel": {
4901                    "grape": "",
4902                }
4903            }
4904        }));
4905
4906        let root_link_path = dir.path().join("root_link");
4907        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
4908        unix::fs::symlink(
4909            &dir.path().join("root/fennel"),
4910            &dir.path().join("root/finnochio"),
4911        )
4912        .unwrap();
4913
4914        let project = Project::test(Arc::new(RealFs), cx);
4915
4916        let (tree, _) = project
4917            .update(cx, |project, cx| {
4918                project.find_or_create_local_worktree(&root_link_path, true, cx)
4919            })
4920            .await
4921            .unwrap();
4922
4923        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
4924            .await;
4925        cx.read(|cx| {
4926            let tree = tree.read(cx);
4927            assert_eq!(tree.file_count(), 5);
4928            assert_eq!(
4929                tree.inode_for_path("fennel/grape"),
4930                tree.inode_for_path("finnochio/grape")
4931            );
4932        });
4933
4934        let cancel_flag = Default::default();
4935        let results = project
4936            .read_with(cx, |project, cx| {
4937                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
4938            })
4939            .await;
4940        assert_eq!(
4941            results
4942                .into_iter()
4943                .map(|result| result.path)
4944                .collect::<Vec<Arc<Path>>>(),
4945            vec![
4946                PathBuf::from("banana/carrot/date").into(),
4947                PathBuf::from("banana/carrot/endive").into(),
4948            ]
4949        );
4950    }
4951
4952    #[gpui::test]
4953    async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
4954        cx.foreground().forbid_parking();
4955
4956        let mut rust_language = Language::new(
4957            LanguageConfig {
4958                name: "Rust".into(),
4959                path_suffixes: vec!["rs".to_string()],
4960                ..Default::default()
4961            },
4962            Some(tree_sitter_rust::language()),
4963        );
4964        let mut json_language = Language::new(
4965            LanguageConfig {
4966                name: "JSON".into(),
4967                path_suffixes: vec!["json".to_string()],
4968                ..Default::default()
4969            },
4970            None,
4971        );
4972        let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(FakeLspAdapter {
4973            name: "the-rust-language-server",
4974            capabilities: lsp::ServerCapabilities {
4975                completion_provider: Some(lsp::CompletionOptions {
4976                    trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
4977                    ..Default::default()
4978                }),
4979                ..Default::default()
4980            },
4981            ..Default::default()
4982        });
4983        let mut fake_json_servers = json_language.set_fake_lsp_adapter(FakeLspAdapter {
4984            name: "the-json-language-server",
4985            capabilities: lsp::ServerCapabilities {
4986                completion_provider: Some(lsp::CompletionOptions {
4987                    trigger_characters: Some(vec![":".to_string()]),
4988                    ..Default::default()
4989                }),
4990                ..Default::default()
4991            },
4992            ..Default::default()
4993        });
4994
4995        let fs = FakeFs::new(cx.background());
4996        fs.insert_tree(
4997            "/the-root",
4998            json!({
4999                "test.rs": "const A: i32 = 1;",
5000                "test2.rs": "",
5001                "Cargo.toml": "a = 1",
5002                "package.json": "{\"a\": 1}",
5003            }),
5004        )
5005        .await;
5006
5007        let project = Project::test(fs.clone(), cx);
5008        project.update(cx, |project, _| {
5009            project.languages.add(Arc::new(rust_language));
5010            project.languages.add(Arc::new(json_language));
5011        });
5012
5013        let worktree_id = project
5014            .update(cx, |project, cx| {
5015                project.find_or_create_local_worktree("/the-root", true, cx)
5016            })
5017            .await
5018            .unwrap()
5019            .0
5020            .read_with(cx, |tree, _| tree.id());
5021
5022        // Open a buffer without an associated language server.
5023        let toml_buffer = project
5024            .update(cx, |project, cx| {
5025                project.open_buffer((worktree_id, "Cargo.toml"), cx)
5026            })
5027            .await
5028            .unwrap();
5029
5030        // Open a buffer with an associated language server.
5031        let rust_buffer = project
5032            .update(cx, |project, cx| {
5033                project.open_buffer((worktree_id, "test.rs"), cx)
5034            })
5035            .await
5036            .unwrap();
5037
5038        // A server is started up, and it is notified about Rust files.
5039        let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
5040        assert_eq!(
5041            fake_rust_server
5042                .receive_notification::<lsp::notification::DidOpenTextDocument>()
5043                .await
5044                .text_document,
5045            lsp::TextDocumentItem {
5046                uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
5047                version: 0,
5048                text: "const A: i32 = 1;".to_string(),
5049                language_id: Default::default()
5050            }
5051        );
5052
5053        // The buffer is configured based on the language server's capabilities.
5054        rust_buffer.read_with(cx, |buffer, _| {
5055            assert_eq!(
5056                buffer.completion_triggers(),
5057                &[".".to_string(), "::".to_string()]
5058            );
5059        });
5060        toml_buffer.read_with(cx, |buffer, _| {
5061            assert!(buffer.completion_triggers().is_empty());
5062        });
5063
5064        // Edit a buffer. The changes are reported to the language server.
5065        rust_buffer.update(cx, |buffer, cx| buffer.edit([16..16], "2", cx));
5066        assert_eq!(
5067            fake_rust_server
5068                .receive_notification::<lsp::notification::DidChangeTextDocument>()
5069                .await
5070                .text_document,
5071            lsp::VersionedTextDocumentIdentifier::new(
5072                lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
5073                1
5074            )
5075        );
5076
5077        // Open a third buffer with a different associated language server.
5078        let json_buffer = project
5079            .update(cx, |project, cx| {
5080                project.open_buffer((worktree_id, "package.json"), cx)
5081            })
5082            .await
5083            .unwrap();
5084
5085        // A json language server is started up and is only notified about the json buffer.
5086        let mut fake_json_server = fake_json_servers.next().await.unwrap();
5087        assert_eq!(
5088            fake_json_server
5089                .receive_notification::<lsp::notification::DidOpenTextDocument>()
5090                .await
5091                .text_document,
5092            lsp::TextDocumentItem {
5093                uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
5094                version: 0,
5095                text: "{\"a\": 1}".to_string(),
5096                language_id: Default::default()
5097            }
5098        );
5099
5100        // This buffer is configured based on the second language server's
5101        // capabilities.
5102        json_buffer.read_with(cx, |buffer, _| {
5103            assert_eq!(buffer.completion_triggers(), &[":".to_string()]);
5104        });
5105
5106        // When opening another buffer whose language server is already running,
5107        // it is also configured based on the existing language server's capabilities.
5108        let rust_buffer2 = project
5109            .update(cx, |project, cx| {
5110                project.open_buffer((worktree_id, "test2.rs"), cx)
5111            })
5112            .await
5113            .unwrap();
5114        rust_buffer2.read_with(cx, |buffer, _| {
5115            assert_eq!(
5116                buffer.completion_triggers(),
5117                &[".".to_string(), "::".to_string()]
5118            );
5119        });
5120
5121        // Changes are reported only to servers matching the buffer's language.
5122        toml_buffer.update(cx, |buffer, cx| buffer.edit([5..5], "23", cx));
5123        rust_buffer2.update(cx, |buffer, cx| buffer.edit([0..0], "let x = 1;", cx));
5124        assert_eq!(
5125            fake_rust_server
5126                .receive_notification::<lsp::notification::DidChangeTextDocument>()
5127                .await
5128                .text_document,
5129            lsp::VersionedTextDocumentIdentifier::new(
5130                lsp::Url::from_file_path("/the-root/test2.rs").unwrap(),
5131                1
5132            )
5133        );
5134
5135        // Save notifications are reported to all servers.
5136        toml_buffer
5137            .update(cx, |buffer, cx| buffer.save(cx))
5138            .await
5139            .unwrap();
5140        assert_eq!(
5141            fake_rust_server
5142                .receive_notification::<lsp::notification::DidSaveTextDocument>()
5143                .await
5144                .text_document,
5145            lsp::TextDocumentIdentifier::new(
5146                lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()
5147            )
5148        );
5149        assert_eq!(
5150            fake_json_server
5151                .receive_notification::<lsp::notification::DidSaveTextDocument>()
5152                .await
5153                .text_document,
5154            lsp::TextDocumentIdentifier::new(
5155                lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()
5156            )
5157        );
5158
5159        // Renames are reported only to servers matching the buffer's language.
5160        fs.rename(
5161            Path::new("/the-root/test2.rs"),
5162            Path::new("/the-root/test3.rs"),
5163            Default::default(),
5164        )
5165        .await
5166        .unwrap();
5167        assert_eq!(
5168            fake_rust_server
5169                .receive_notification::<lsp::notification::DidCloseTextDocument>()
5170                .await
5171                .text_document,
5172            lsp::TextDocumentIdentifier::new(
5173                lsp::Url::from_file_path("/the-root/test2.rs").unwrap()
5174            ),
5175        );
5176        assert_eq!(
5177            fake_rust_server
5178                .receive_notification::<lsp::notification::DidOpenTextDocument>()
5179                .await
5180                .text_document,
5181            lsp::TextDocumentItem {
5182                uri: lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),
5183                version: 0,
5184                text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
5185                language_id: Default::default()
5186            },
5187        );
5188
5189        rust_buffer2.update(cx, |buffer, cx| {
5190            buffer.update_diagnostics(
5191                DiagnosticSet::from_sorted_entries(
5192                    vec![DiagnosticEntry {
5193                        diagnostic: Default::default(),
5194                        range: Anchor::MIN..Anchor::MAX,
5195                    }],
5196                    &buffer.snapshot(),
5197                ),
5198                cx,
5199            );
5200            assert_eq!(
5201                buffer
5202                    .snapshot()
5203                    .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
5204                    .count(),
5205                1
5206            );
5207        });
5208
5209        // When the rename changes the extension of the file, the buffer gets closed on the old
5210        // language server and gets opened on the new one.
5211        fs.rename(
5212            Path::new("/the-root/test3.rs"),
5213            Path::new("/the-root/test3.json"),
5214            Default::default(),
5215        )
5216        .await
5217        .unwrap();
5218        assert_eq!(
5219            fake_rust_server
5220                .receive_notification::<lsp::notification::DidCloseTextDocument>()
5221                .await
5222                .text_document,
5223            lsp::TextDocumentIdentifier::new(
5224                lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),
5225            ),
5226        );
5227        assert_eq!(
5228            fake_json_server
5229                .receive_notification::<lsp::notification::DidOpenTextDocument>()
5230                .await
5231                .text_document,
5232            lsp::TextDocumentItem {
5233                uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
5234                version: 0,
5235                text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
5236                language_id: Default::default()
5237            },
5238        );
5239        // We clear the diagnostics, since the language has changed.
5240        rust_buffer2.read_with(cx, |buffer, _| {
5241            assert_eq!(
5242                buffer
5243                    .snapshot()
5244                    .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
5245                    .count(),
5246                0
5247            );
5248        });
5249
5250        // The renamed file's version resets after changing language server.
5251        rust_buffer2.update(cx, |buffer, cx| buffer.edit([0..0], "// ", cx));
5252        assert_eq!(
5253            fake_json_server
5254                .receive_notification::<lsp::notification::DidChangeTextDocument>()
5255                .await
5256                .text_document,
5257            lsp::VersionedTextDocumentIdentifier::new(
5258                lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
5259                1
5260            )
5261        );
5262
5263        // Restart language servers
5264        project.update(cx, |project, cx| {
5265            project.restart_language_servers_for_buffers(
5266                vec![rust_buffer.clone(), json_buffer.clone()],
5267                cx,
5268            );
5269        });
5270
5271        let mut rust_shutdown_requests = fake_rust_server
5272            .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
5273        let mut json_shutdown_requests = fake_json_server
5274            .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
5275        futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next());
5276
5277        let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
5278        let mut fake_json_server = fake_json_servers.next().await.unwrap();
5279
5280        // Ensure rust document is reopened in new rust language server
5281        assert_eq!(
5282            fake_rust_server
5283                .receive_notification::<lsp::notification::DidOpenTextDocument>()
5284                .await
5285                .text_document,
5286            lsp::TextDocumentItem {
5287                uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
5288                version: 1,
5289                text: rust_buffer.read_with(cx, |buffer, _| buffer.text()),
5290                language_id: Default::default()
5291            }
5292        );
5293
5294        // Ensure json documents are reopened in new json language server
5295        assert_set_eq!(
5296            [
5297                fake_json_server
5298                    .receive_notification::<lsp::notification::DidOpenTextDocument>()
5299                    .await
5300                    .text_document,
5301                fake_json_server
5302                    .receive_notification::<lsp::notification::DidOpenTextDocument>()
5303                    .await
5304                    .text_document,
5305            ],
5306            [
5307                lsp::TextDocumentItem {
5308                    uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
5309                    version: 0,
5310                    text: json_buffer.read_with(cx, |buffer, _| buffer.text()),
5311                    language_id: Default::default()
5312                },
5313                lsp::TextDocumentItem {
5314                    uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
5315                    version: 1,
5316                    text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
5317                    language_id: Default::default()
5318                }
5319            ]
5320        );
5321
5322        // Close notifications are reported only to servers matching the buffer's language.
5323        cx.update(|_| drop(json_buffer));
5324        let close_message = lsp::DidCloseTextDocumentParams {
5325            text_document: lsp::TextDocumentIdentifier::new(
5326                lsp::Url::from_file_path("/the-root/package.json").unwrap(),
5327            ),
5328        };
5329        assert_eq!(
5330            fake_json_server
5331                .receive_notification::<lsp::notification::DidCloseTextDocument>()
5332                .await,
5333            close_message,
5334        );
5335    }
5336
5337    #[gpui::test]
5338    async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
5339        cx.foreground().forbid_parking();
5340
5341        let fs = FakeFs::new(cx.background());
5342        fs.insert_tree(
5343            "/dir",
5344            json!({
5345                "a.rs": "let a = 1;",
5346                "b.rs": "let b = 2;"
5347            }),
5348        )
5349        .await;
5350
5351        let project = Project::test(fs, cx);
5352        let worktree_a_id = project
5353            .update(cx, |project, cx| {
5354                project.find_or_create_local_worktree("/dir/a.rs", true, cx)
5355            })
5356            .await
5357            .unwrap()
5358            .0
5359            .read_with(cx, |tree, _| tree.id());
5360        let worktree_b_id = project
5361            .update(cx, |project, cx| {
5362                project.find_or_create_local_worktree("/dir/b.rs", true, cx)
5363            })
5364            .await
5365            .unwrap()
5366            .0
5367            .read_with(cx, |tree, _| tree.id());
5368
5369        let buffer_a = project
5370            .update(cx, |project, cx| {
5371                project.open_buffer((worktree_a_id, ""), cx)
5372            })
5373            .await
5374            .unwrap();
5375        let buffer_b = project
5376            .update(cx, |project, cx| {
5377                project.open_buffer((worktree_b_id, ""), cx)
5378            })
5379            .await
5380            .unwrap();
5381
5382        project.update(cx, |project, cx| {
5383            project
5384                .update_diagnostics(
5385                    lsp::PublishDiagnosticsParams {
5386                        uri: Url::from_file_path("/dir/a.rs").unwrap(),
5387                        version: None,
5388                        diagnostics: vec![lsp::Diagnostic {
5389                            range: lsp::Range::new(
5390                                lsp::Position::new(0, 4),
5391                                lsp::Position::new(0, 5),
5392                            ),
5393                            severity: Some(lsp::DiagnosticSeverity::ERROR),
5394                            message: "error 1".to_string(),
5395                            ..Default::default()
5396                        }],
5397                    },
5398                    &[],
5399                    cx,
5400                )
5401                .unwrap();
5402            project
5403                .update_diagnostics(
5404                    lsp::PublishDiagnosticsParams {
5405                        uri: Url::from_file_path("/dir/b.rs").unwrap(),
5406                        version: None,
5407                        diagnostics: vec![lsp::Diagnostic {
5408                            range: lsp::Range::new(
5409                                lsp::Position::new(0, 4),
5410                                lsp::Position::new(0, 5),
5411                            ),
5412                            severity: Some(lsp::DiagnosticSeverity::WARNING),
5413                            message: "error 2".to_string(),
5414                            ..Default::default()
5415                        }],
5416                    },
5417                    &[],
5418                    cx,
5419                )
5420                .unwrap();
5421        });
5422
5423        buffer_a.read_with(cx, |buffer, _| {
5424            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
5425            assert_eq!(
5426                chunks
5427                    .iter()
5428                    .map(|(s, d)| (s.as_str(), *d))
5429                    .collect::<Vec<_>>(),
5430                &[
5431                    ("let ", None),
5432                    ("a", Some(DiagnosticSeverity::ERROR)),
5433                    (" = 1;", None),
5434                ]
5435            );
5436        });
5437        buffer_b.read_with(cx, |buffer, _| {
5438            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
5439            assert_eq!(
5440                chunks
5441                    .iter()
5442                    .map(|(s, d)| (s.as_str(), *d))
5443                    .collect::<Vec<_>>(),
5444                &[
5445                    ("let ", None),
5446                    ("b", Some(DiagnosticSeverity::WARNING)),
5447                    (" = 2;", None),
5448                ]
5449            );
5450        });
5451    }
5452
5453    #[gpui::test]
5454    async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
5455        cx.foreground().forbid_parking();
5456
5457        let progress_token = "the-progress-token";
5458        let mut language = Language::new(
5459            LanguageConfig {
5460                name: "Rust".into(),
5461                path_suffixes: vec!["rs".to_string()],
5462                ..Default::default()
5463            },
5464            Some(tree_sitter_rust::language()),
5465        );
5466        let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
5467            disk_based_diagnostics_progress_token: Some(progress_token),
5468            disk_based_diagnostics_sources: &["disk"],
5469            ..Default::default()
5470        });
5471
5472        let fs = FakeFs::new(cx.background());
5473        fs.insert_tree(
5474            "/dir",
5475            json!({
5476                "a.rs": "fn a() { A }",
5477                "b.rs": "const y: i32 = 1",
5478            }),
5479        )
5480        .await;
5481
5482        let project = Project::test(fs, cx);
5483        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
5484
5485        let (tree, _) = project
5486            .update(cx, |project, cx| {
5487                project.find_or_create_local_worktree("/dir", true, cx)
5488            })
5489            .await
5490            .unwrap();
5491        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
5492
5493        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
5494            .await;
5495
5496        // Cause worktree to start the fake language server
5497        let _buffer = project
5498            .update(cx, |project, cx| {
5499                project.open_buffer((worktree_id, Path::new("b.rs")), cx)
5500            })
5501            .await
5502            .unwrap();
5503
5504        let mut events = subscribe(&project, cx);
5505
5506        let mut fake_server = fake_servers.next().await.unwrap();
5507        fake_server.start_progress(progress_token).await;
5508        assert_eq!(
5509            events.next().await.unwrap(),
5510            Event::DiskBasedDiagnosticsStarted
5511        );
5512
5513        fake_server.start_progress(progress_token).await;
5514        fake_server.end_progress(progress_token).await;
5515        fake_server.start_progress(progress_token).await;
5516
5517        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5518            lsp::PublishDiagnosticsParams {
5519                uri: Url::from_file_path("/dir/a.rs").unwrap(),
5520                version: None,
5521                diagnostics: vec![lsp::Diagnostic {
5522                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
5523                    severity: Some(lsp::DiagnosticSeverity::ERROR),
5524                    message: "undefined variable 'A'".to_string(),
5525                    ..Default::default()
5526                }],
5527            },
5528        );
5529        assert_eq!(
5530            events.next().await.unwrap(),
5531            Event::DiagnosticsUpdated((worktree_id, Path::new("a.rs")).into())
5532        );
5533
5534        fake_server.end_progress(progress_token).await;
5535        fake_server.end_progress(progress_token).await;
5536        assert_eq!(
5537            events.next().await.unwrap(),
5538            Event::DiskBasedDiagnosticsUpdated
5539        );
5540        assert_eq!(
5541            events.next().await.unwrap(),
5542            Event::DiskBasedDiagnosticsFinished
5543        );
5544
5545        let buffer = project
5546            .update(cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
5547            .await
5548            .unwrap();
5549
5550        buffer.read_with(cx, |buffer, _| {
5551            let snapshot = buffer.snapshot();
5552            let diagnostics = snapshot
5553                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
5554                .collect::<Vec<_>>();
5555            assert_eq!(
5556                diagnostics,
5557                &[DiagnosticEntry {
5558                    range: Point::new(0, 9)..Point::new(0, 10),
5559                    diagnostic: Diagnostic {
5560                        severity: lsp::DiagnosticSeverity::ERROR,
5561                        message: "undefined variable 'A'".to_string(),
5562                        group_id: 0,
5563                        is_primary: true,
5564                        ..Default::default()
5565                    }
5566                }]
5567            )
5568        });
5569
5570        // Ensure publishing empty diagnostics twice only results in one update event.
5571        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5572            lsp::PublishDiagnosticsParams {
5573                uri: Url::from_file_path("/dir/a.rs").unwrap(),
5574                version: None,
5575                diagnostics: Default::default(),
5576            },
5577        );
5578        assert_eq!(
5579            events.next().await.unwrap(),
5580            Event::DiagnosticsUpdated((worktree_id, Path::new("a.rs")).into())
5581        );
5582
5583        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5584            lsp::PublishDiagnosticsParams {
5585                uri: Url::from_file_path("/dir/a.rs").unwrap(),
5586                version: None,
5587                diagnostics: Default::default(),
5588            },
5589        );
5590        cx.foreground().run_until_parked();
5591        assert_eq!(futures::poll!(events.next()), Poll::Pending);
5592    }
5593
5594    #[gpui::test]
5595    async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) {
5596        cx.foreground().forbid_parking();
5597
5598        let progress_token = "the-progress-token";
5599        let mut language = Language::new(
5600            LanguageConfig {
5601                path_suffixes: vec!["rs".to_string()],
5602                ..Default::default()
5603            },
5604            None,
5605        );
5606        let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
5607            disk_based_diagnostics_sources: &["disk"],
5608            disk_based_diagnostics_progress_token: Some(progress_token),
5609            ..Default::default()
5610        });
5611
5612        let fs = FakeFs::new(cx.background());
5613        fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
5614
5615        let project = Project::test(fs, cx);
5616        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
5617
5618        let worktree_id = project
5619            .update(cx, |project, cx| {
5620                project.find_or_create_local_worktree("/dir", true, cx)
5621            })
5622            .await
5623            .unwrap()
5624            .0
5625            .read_with(cx, |tree, _| tree.id());
5626
5627        let buffer = project
5628            .update(cx, |project, cx| {
5629                project.open_buffer((worktree_id, "a.rs"), cx)
5630            })
5631            .await
5632            .unwrap();
5633
5634        // Simulate diagnostics starting to update.
5635        let mut fake_server = fake_servers.next().await.unwrap();
5636        fake_server.start_progress(progress_token).await;
5637
5638        // Restart the server before the diagnostics finish updating.
5639        project.update(cx, |project, cx| {
5640            project.restart_language_servers_for_buffers([buffer], cx);
5641        });
5642        let mut events = subscribe(&project, cx);
5643
5644        // Simulate the newly started server sending more diagnostics.
5645        let mut fake_server = fake_servers.next().await.unwrap();
5646        fake_server.start_progress(progress_token).await;
5647        assert_eq!(
5648            events.next().await.unwrap(),
5649            Event::DiskBasedDiagnosticsStarted
5650        );
5651
5652        // All diagnostics are considered done, despite the old server's diagnostic
5653        // task never completing.
5654        fake_server.end_progress(progress_token).await;
5655        assert_eq!(
5656            events.next().await.unwrap(),
5657            Event::DiskBasedDiagnosticsUpdated
5658        );
5659        assert_eq!(
5660            events.next().await.unwrap(),
5661            Event::DiskBasedDiagnosticsFinished
5662        );
5663        project.read_with(cx, |project, _| {
5664            assert!(!project.is_running_disk_based_diagnostics());
5665        });
5666    }
5667
5668    #[gpui::test]
5669    async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
5670        cx.foreground().forbid_parking();
5671
5672        let mut language = Language::new(
5673            LanguageConfig {
5674                name: "Rust".into(),
5675                path_suffixes: vec!["rs".to_string()],
5676                ..Default::default()
5677            },
5678            Some(tree_sitter_rust::language()),
5679        );
5680        let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
5681            disk_based_diagnostics_sources: &["disk"],
5682            ..Default::default()
5683        });
5684
5685        let text = "
5686            fn a() { A }
5687            fn b() { BB }
5688            fn c() { CCC }
5689        "
5690        .unindent();
5691
5692        let fs = FakeFs::new(cx.background());
5693        fs.insert_tree("/dir", json!({ "a.rs": text })).await;
5694
5695        let project = Project::test(fs, cx);
5696        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
5697
5698        let worktree_id = project
5699            .update(cx, |project, cx| {
5700                project.find_or_create_local_worktree("/dir", true, cx)
5701            })
5702            .await
5703            .unwrap()
5704            .0
5705            .read_with(cx, |tree, _| tree.id());
5706
5707        let buffer = project
5708            .update(cx, |project, cx| {
5709                project.open_buffer((worktree_id, "a.rs"), cx)
5710            })
5711            .await
5712            .unwrap();
5713
5714        let mut fake_server = fake_servers.next().await.unwrap();
5715        let open_notification = fake_server
5716            .receive_notification::<lsp::notification::DidOpenTextDocument>()
5717            .await;
5718
5719        // Edit the buffer, moving the content down
5720        buffer.update(cx, |buffer, cx| buffer.edit([0..0], "\n\n", cx));
5721        let change_notification_1 = fake_server
5722            .receive_notification::<lsp::notification::DidChangeTextDocument>()
5723            .await;
5724        assert!(
5725            change_notification_1.text_document.version > open_notification.text_document.version
5726        );
5727
5728        // Report some diagnostics for the initial version of the buffer
5729        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5730            lsp::PublishDiagnosticsParams {
5731                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
5732                version: Some(open_notification.text_document.version),
5733                diagnostics: vec![
5734                    lsp::Diagnostic {
5735                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
5736                        severity: Some(DiagnosticSeverity::ERROR),
5737                        message: "undefined variable 'A'".to_string(),
5738                        source: Some("disk".to_string()),
5739                        ..Default::default()
5740                    },
5741                    lsp::Diagnostic {
5742                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
5743                        severity: Some(DiagnosticSeverity::ERROR),
5744                        message: "undefined variable 'BB'".to_string(),
5745                        source: Some("disk".to_string()),
5746                        ..Default::default()
5747                    },
5748                    lsp::Diagnostic {
5749                        range: lsp::Range::new(lsp::Position::new(2, 9), lsp::Position::new(2, 12)),
5750                        severity: Some(DiagnosticSeverity::ERROR),
5751                        source: Some("disk".to_string()),
5752                        message: "undefined variable 'CCC'".to_string(),
5753                        ..Default::default()
5754                    },
5755                ],
5756            },
5757        );
5758
5759        // The diagnostics have moved down since they were created.
5760        buffer.next_notification(cx).await;
5761        buffer.read_with(cx, |buffer, _| {
5762            assert_eq!(
5763                buffer
5764                    .snapshot()
5765                    .diagnostics_in_range::<_, Point>(Point::new(3, 0)..Point::new(5, 0), false)
5766                    .collect::<Vec<_>>(),
5767                &[
5768                    DiagnosticEntry {
5769                        range: Point::new(3, 9)..Point::new(3, 11),
5770                        diagnostic: Diagnostic {
5771                            severity: DiagnosticSeverity::ERROR,
5772                            message: "undefined variable 'BB'".to_string(),
5773                            is_disk_based: true,
5774                            group_id: 1,
5775                            is_primary: true,
5776                            ..Default::default()
5777                        },
5778                    },
5779                    DiagnosticEntry {
5780                        range: Point::new(4, 9)..Point::new(4, 12),
5781                        diagnostic: Diagnostic {
5782                            severity: DiagnosticSeverity::ERROR,
5783                            message: "undefined variable 'CCC'".to_string(),
5784                            is_disk_based: true,
5785                            group_id: 2,
5786                            is_primary: true,
5787                            ..Default::default()
5788                        }
5789                    }
5790                ]
5791            );
5792            assert_eq!(
5793                chunks_with_diagnostics(buffer, 0..buffer.len()),
5794                [
5795                    ("\n\nfn a() { ".to_string(), None),
5796                    ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
5797                    (" }\nfn b() { ".to_string(), None),
5798                    ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
5799                    (" }\nfn c() { ".to_string(), None),
5800                    ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
5801                    (" }\n".to_string(), None),
5802                ]
5803            );
5804            assert_eq!(
5805                chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
5806                [
5807                    ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
5808                    (" }\nfn c() { ".to_string(), None),
5809                    ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
5810                ]
5811            );
5812        });
5813
5814        // Ensure overlapping diagnostics are highlighted correctly.
5815        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5816            lsp::PublishDiagnosticsParams {
5817                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
5818                version: Some(open_notification.text_document.version),
5819                diagnostics: vec![
5820                    lsp::Diagnostic {
5821                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
5822                        severity: Some(DiagnosticSeverity::ERROR),
5823                        message: "undefined variable 'A'".to_string(),
5824                        source: Some("disk".to_string()),
5825                        ..Default::default()
5826                    },
5827                    lsp::Diagnostic {
5828                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
5829                        severity: Some(DiagnosticSeverity::WARNING),
5830                        message: "unreachable statement".to_string(),
5831                        source: Some("disk".to_string()),
5832                        ..Default::default()
5833                    },
5834                ],
5835            },
5836        );
5837
5838        buffer.next_notification(cx).await;
5839        buffer.read_with(cx, |buffer, _| {
5840            assert_eq!(
5841                buffer
5842                    .snapshot()
5843                    .diagnostics_in_range::<_, Point>(Point::new(2, 0)..Point::new(3, 0), false)
5844                    .collect::<Vec<_>>(),
5845                &[
5846                    DiagnosticEntry {
5847                        range: Point::new(2, 9)..Point::new(2, 12),
5848                        diagnostic: Diagnostic {
5849                            severity: DiagnosticSeverity::WARNING,
5850                            message: "unreachable statement".to_string(),
5851                            is_disk_based: true,
5852                            group_id: 1,
5853                            is_primary: true,
5854                            ..Default::default()
5855                        }
5856                    },
5857                    DiagnosticEntry {
5858                        range: Point::new(2, 9)..Point::new(2, 10),
5859                        diagnostic: Diagnostic {
5860                            severity: DiagnosticSeverity::ERROR,
5861                            message: "undefined variable 'A'".to_string(),
5862                            is_disk_based: true,
5863                            group_id: 0,
5864                            is_primary: true,
5865                            ..Default::default()
5866                        },
5867                    }
5868                ]
5869            );
5870            assert_eq!(
5871                chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
5872                [
5873                    ("fn a() { ".to_string(), None),
5874                    ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
5875                    (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
5876                    ("\n".to_string(), None),
5877                ]
5878            );
5879            assert_eq!(
5880                chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
5881                [
5882                    (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
5883                    ("\n".to_string(), None),
5884                ]
5885            );
5886        });
5887
5888        // Keep editing the buffer and ensure disk-based diagnostics get translated according to the
5889        // changes since the last save.
5890        buffer.update(cx, |buffer, cx| {
5891            buffer.edit(Some(Point::new(2, 0)..Point::new(2, 0)), "    ", cx);
5892            buffer.edit(Some(Point::new(2, 8)..Point::new(2, 10)), "(x: usize)", cx);
5893            buffer.edit(Some(Point::new(3, 10)..Point::new(3, 10)), "xxx", cx);
5894        });
5895        let change_notification_2 = fake_server
5896            .receive_notification::<lsp::notification::DidChangeTextDocument>()
5897            .await;
5898        assert!(
5899            change_notification_2.text_document.version
5900                > change_notification_1.text_document.version
5901        );
5902
5903        // Handle out-of-order diagnostics
5904        fake_server.notify::<lsp::notification::PublishDiagnostics>(
5905            lsp::PublishDiagnosticsParams {
5906                uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
5907                version: Some(change_notification_2.text_document.version),
5908                diagnostics: vec![
5909                    lsp::Diagnostic {
5910                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
5911                        severity: Some(DiagnosticSeverity::ERROR),
5912                        message: "undefined variable 'BB'".to_string(),
5913                        source: Some("disk".to_string()),
5914                        ..Default::default()
5915                    },
5916                    lsp::Diagnostic {
5917                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
5918                        severity: Some(DiagnosticSeverity::WARNING),
5919                        message: "undefined variable 'A'".to_string(),
5920                        source: Some("disk".to_string()),
5921                        ..Default::default()
5922                    },
5923                ],
5924            },
5925        );
5926
5927        buffer.next_notification(cx).await;
5928        buffer.read_with(cx, |buffer, _| {
5929            assert_eq!(
5930                buffer
5931                    .snapshot()
5932                    .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
5933                    .collect::<Vec<_>>(),
5934                &[
5935                    DiagnosticEntry {
5936                        range: Point::new(2, 21)..Point::new(2, 22),
5937                        diagnostic: Diagnostic {
5938                            severity: DiagnosticSeverity::WARNING,
5939                            message: "undefined variable 'A'".to_string(),
5940                            is_disk_based: true,
5941                            group_id: 1,
5942                            is_primary: true,
5943                            ..Default::default()
5944                        }
5945                    },
5946                    DiagnosticEntry {
5947                        range: Point::new(3, 9)..Point::new(3, 14),
5948                        diagnostic: Diagnostic {
5949                            severity: DiagnosticSeverity::ERROR,
5950                            message: "undefined variable 'BB'".to_string(),
5951                            is_disk_based: true,
5952                            group_id: 0,
5953                            is_primary: true,
5954                            ..Default::default()
5955                        },
5956                    }
5957                ]
5958            );
5959        });
5960    }
5961
5962    #[gpui::test]
5963    async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
5964        cx.foreground().forbid_parking();
5965
5966        let text = concat!(
5967            "let one = ;\n", //
5968            "let two = \n",
5969            "let three = 3;\n",
5970        );
5971
5972        let fs = FakeFs::new(cx.background());
5973        fs.insert_tree("/dir", json!({ "a.rs": text })).await;
5974
5975        let project = Project::test(fs, cx);
5976        let worktree_id = project
5977            .update(cx, |project, cx| {
5978                project.find_or_create_local_worktree("/dir", true, cx)
5979            })
5980            .await
5981            .unwrap()
5982            .0
5983            .read_with(cx, |tree, _| tree.id());
5984
5985        let buffer = project
5986            .update(cx, |project, cx| {
5987                project.open_buffer((worktree_id, "a.rs"), cx)
5988            })
5989            .await
5990            .unwrap();
5991
5992        project.update(cx, |project, cx| {
5993            project
5994                .update_buffer_diagnostics(
5995                    &buffer,
5996                    vec![
5997                        DiagnosticEntry {
5998                            range: PointUtf16::new(0, 10)..PointUtf16::new(0, 10),
5999                            diagnostic: Diagnostic {
6000                                severity: DiagnosticSeverity::ERROR,
6001                                message: "syntax error 1".to_string(),
6002                                ..Default::default()
6003                            },
6004                        },
6005                        DiagnosticEntry {
6006                            range: PointUtf16::new(1, 10)..PointUtf16::new(1, 10),
6007                            diagnostic: Diagnostic {
6008                                severity: DiagnosticSeverity::ERROR,
6009                                message: "syntax error 2".to_string(),
6010                                ..Default::default()
6011                            },
6012                        },
6013                    ],
6014                    None,
6015                    cx,
6016                )
6017                .unwrap();
6018        });
6019
6020        // An empty range is extended forward to include the following character.
6021        // At the end of a line, an empty range is extended backward to include
6022        // the preceding character.
6023        buffer.read_with(cx, |buffer, _| {
6024            let chunks = chunks_with_diagnostics(&buffer, 0..buffer.len());
6025            assert_eq!(
6026                chunks
6027                    .iter()
6028                    .map(|(s, d)| (s.as_str(), *d))
6029                    .collect::<Vec<_>>(),
6030                &[
6031                    ("let one = ", None),
6032                    (";", Some(DiagnosticSeverity::ERROR)),
6033                    ("\nlet two =", None),
6034                    (" ", Some(DiagnosticSeverity::ERROR)),
6035                    ("\nlet three = 3;\n", None)
6036                ]
6037            );
6038        });
6039    }
6040
6041    #[gpui::test]
6042    async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) {
6043        cx.foreground().forbid_parking();
6044
6045        let mut language = Language::new(
6046            LanguageConfig {
6047                name: "Rust".into(),
6048                path_suffixes: vec!["rs".to_string()],
6049                ..Default::default()
6050            },
6051            Some(tree_sitter_rust::language()),
6052        );
6053        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
6054
6055        let text = "
6056            fn a() {
6057                f1();
6058            }
6059            fn b() {
6060                f2();
6061            }
6062            fn c() {
6063                f3();
6064            }
6065        "
6066        .unindent();
6067
6068        let fs = FakeFs::new(cx.background());
6069        fs.insert_tree(
6070            "/dir",
6071            json!({
6072                "a.rs": text.clone(),
6073            }),
6074        )
6075        .await;
6076
6077        let project = Project::test(fs, cx);
6078        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
6079
6080        let worktree_id = project
6081            .update(cx, |project, cx| {
6082                project.find_or_create_local_worktree("/dir", true, cx)
6083            })
6084            .await
6085            .unwrap()
6086            .0
6087            .read_with(cx, |tree, _| tree.id());
6088
6089        let buffer = project
6090            .update(cx, |project, cx| {
6091                project.open_buffer((worktree_id, "a.rs"), cx)
6092            })
6093            .await
6094            .unwrap();
6095
6096        let mut fake_server = fake_servers.next().await.unwrap();
6097        let lsp_document_version = fake_server
6098            .receive_notification::<lsp::notification::DidOpenTextDocument>()
6099            .await
6100            .text_document
6101            .version;
6102
6103        // Simulate editing the buffer after the language server computes some edits.
6104        buffer.update(cx, |buffer, cx| {
6105            buffer.edit(
6106                [Point::new(0, 0)..Point::new(0, 0)],
6107                "// above first function\n",
6108                cx,
6109            );
6110            buffer.edit(
6111                [Point::new(2, 0)..Point::new(2, 0)],
6112                "    // inside first function\n",
6113                cx,
6114            );
6115            buffer.edit(
6116                [Point::new(6, 4)..Point::new(6, 4)],
6117                "// inside second function ",
6118                cx,
6119            );
6120
6121            assert_eq!(
6122                buffer.text(),
6123                "
6124                    // above first function
6125                    fn a() {
6126                        // inside first function
6127                        f1();
6128                    }
6129                    fn b() {
6130                        // inside second function f2();
6131                    }
6132                    fn c() {
6133                        f3();
6134                    }
6135                "
6136                .unindent()
6137            );
6138        });
6139
6140        let edits = project
6141            .update(cx, |project, cx| {
6142                project.edits_from_lsp(
6143                    &buffer,
6144                    vec![
6145                        // replace body of first function
6146                        lsp::TextEdit {
6147                            range: lsp::Range::new(
6148                                lsp::Position::new(0, 0),
6149                                lsp::Position::new(3, 0),
6150                            ),
6151                            new_text: "
6152                                fn a() {
6153                                    f10();
6154                                }
6155                            "
6156                            .unindent(),
6157                        },
6158                        // edit inside second function
6159                        lsp::TextEdit {
6160                            range: lsp::Range::new(
6161                                lsp::Position::new(4, 6),
6162                                lsp::Position::new(4, 6),
6163                            ),
6164                            new_text: "00".into(),
6165                        },
6166                        // edit inside third function via two distinct edits
6167                        lsp::TextEdit {
6168                            range: lsp::Range::new(
6169                                lsp::Position::new(7, 5),
6170                                lsp::Position::new(7, 5),
6171                            ),
6172                            new_text: "4000".into(),
6173                        },
6174                        lsp::TextEdit {
6175                            range: lsp::Range::new(
6176                                lsp::Position::new(7, 5),
6177                                lsp::Position::new(7, 6),
6178                            ),
6179                            new_text: "".into(),
6180                        },
6181                    ],
6182                    Some(lsp_document_version),
6183                    cx,
6184                )
6185            })
6186            .await
6187            .unwrap();
6188
6189        buffer.update(cx, |buffer, cx| {
6190            for (range, new_text) in edits {
6191                buffer.edit([range], new_text, cx);
6192            }
6193            assert_eq!(
6194                buffer.text(),
6195                "
6196                    // above first function
6197                    fn a() {
6198                        // inside first function
6199                        f10();
6200                    }
6201                    fn b() {
6202                        // inside second function f200();
6203                    }
6204                    fn c() {
6205                        f4000();
6206                    }
6207                "
6208                .unindent()
6209            );
6210        });
6211    }
6212
6213    #[gpui::test]
6214    async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) {
6215        cx.foreground().forbid_parking();
6216
6217        let text = "
6218            use a::b;
6219            use a::c;
6220
6221            fn f() {
6222                b();
6223                c();
6224            }
6225        "
6226        .unindent();
6227
6228        let fs = FakeFs::new(cx.background());
6229        fs.insert_tree(
6230            "/dir",
6231            json!({
6232                "a.rs": text.clone(),
6233            }),
6234        )
6235        .await;
6236
6237        let project = Project::test(fs, cx);
6238        let worktree_id = project
6239            .update(cx, |project, cx| {
6240                project.find_or_create_local_worktree("/dir", true, cx)
6241            })
6242            .await
6243            .unwrap()
6244            .0
6245            .read_with(cx, |tree, _| tree.id());
6246
6247        let buffer = project
6248            .update(cx, |project, cx| {
6249                project.open_buffer((worktree_id, "a.rs"), cx)
6250            })
6251            .await
6252            .unwrap();
6253
6254        // Simulate the language server sending us a small edit in the form of a very large diff.
6255        // Rust-analyzer does this when performing a merge-imports code action.
6256        let edits = project
6257            .update(cx, |project, cx| {
6258                project.edits_from_lsp(
6259                    &buffer,
6260                    [
6261                        // Replace the first use statement without editing the semicolon.
6262                        lsp::TextEdit {
6263                            range: lsp::Range::new(
6264                                lsp::Position::new(0, 4),
6265                                lsp::Position::new(0, 8),
6266                            ),
6267                            new_text: "a::{b, c}".into(),
6268                        },
6269                        // Reinsert the remainder of the file between the semicolon and the final
6270                        // newline of the file.
6271                        lsp::TextEdit {
6272                            range: lsp::Range::new(
6273                                lsp::Position::new(0, 9),
6274                                lsp::Position::new(0, 9),
6275                            ),
6276                            new_text: "\n\n".into(),
6277                        },
6278                        lsp::TextEdit {
6279                            range: lsp::Range::new(
6280                                lsp::Position::new(0, 9),
6281                                lsp::Position::new(0, 9),
6282                            ),
6283                            new_text: "
6284                                fn f() {
6285                                    b();
6286                                    c();
6287                                }"
6288                            .unindent(),
6289                        },
6290                        // Delete everything after the first newline of the file.
6291                        lsp::TextEdit {
6292                            range: lsp::Range::new(
6293                                lsp::Position::new(1, 0),
6294                                lsp::Position::new(7, 0),
6295                            ),
6296                            new_text: "".into(),
6297                        },
6298                    ],
6299                    None,
6300                    cx,
6301                )
6302            })
6303            .await
6304            .unwrap();
6305
6306        buffer.update(cx, |buffer, cx| {
6307            let edits = edits
6308                .into_iter()
6309                .map(|(range, text)| {
6310                    (
6311                        range.start.to_point(&buffer)..range.end.to_point(&buffer),
6312                        text,
6313                    )
6314                })
6315                .collect::<Vec<_>>();
6316
6317            assert_eq!(
6318                edits,
6319                [
6320                    (Point::new(0, 4)..Point::new(0, 8), "a::{b, c}".into()),
6321                    (Point::new(1, 0)..Point::new(2, 0), "".into())
6322                ]
6323            );
6324
6325            for (range, new_text) in edits {
6326                buffer.edit([range], new_text, cx);
6327            }
6328            assert_eq!(
6329                buffer.text(),
6330                "
6331                    use a::{b, c};
6332
6333                    fn f() {
6334                        b();
6335                        c();
6336                    }
6337                "
6338                .unindent()
6339            );
6340        });
6341    }
6342
6343    fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
6344        buffer: &Buffer,
6345        range: Range<T>,
6346    ) -> Vec<(String, Option<DiagnosticSeverity>)> {
6347        let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
6348        for chunk in buffer.snapshot().chunks(range, true) {
6349            if chunks.last().map_or(false, |prev_chunk| {
6350                prev_chunk.1 == chunk.diagnostic_severity
6351            }) {
6352                chunks.last_mut().unwrap().0.push_str(chunk.text);
6353            } else {
6354                chunks.push((chunk.text.to_string(), chunk.diagnostic_severity));
6355            }
6356        }
6357        chunks
6358    }
6359
6360    #[gpui::test]
6361    async fn test_search_worktree_without_files(cx: &mut gpui::TestAppContext) {
6362        let dir = temp_tree(json!({
6363            "root": {
6364                "dir1": {},
6365                "dir2": {
6366                    "dir3": {}
6367                }
6368            }
6369        }));
6370
6371        let project = Project::test(Arc::new(RealFs), cx);
6372        let (tree, _) = project
6373            .update(cx, |project, cx| {
6374                project.find_or_create_local_worktree(&dir.path(), true, cx)
6375            })
6376            .await
6377            .unwrap();
6378
6379        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
6380            .await;
6381
6382        let cancel_flag = Default::default();
6383        let results = project
6384            .read_with(cx, |project, cx| {
6385                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
6386            })
6387            .await;
6388
6389        assert!(results.is_empty());
6390    }
6391
6392    #[gpui::test]
6393    async fn test_definition(cx: &mut gpui::TestAppContext) {
6394        let mut language = Language::new(
6395            LanguageConfig {
6396                name: "Rust".into(),
6397                path_suffixes: vec!["rs".to_string()],
6398                ..Default::default()
6399            },
6400            Some(tree_sitter_rust::language()),
6401        );
6402        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
6403
6404        let fs = FakeFs::new(cx.background());
6405        fs.insert_tree(
6406            "/dir",
6407            json!({
6408                "a.rs": "const fn a() { A }",
6409                "b.rs": "const y: i32 = crate::a()",
6410            }),
6411        )
6412        .await;
6413
6414        let project = Project::test(fs, cx);
6415        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
6416
6417        let (tree, _) = project
6418            .update(cx, |project, cx| {
6419                project.find_or_create_local_worktree("/dir/b.rs", true, cx)
6420            })
6421            .await
6422            .unwrap();
6423        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
6424        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
6425            .await;
6426
6427        let buffer = project
6428            .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
6429            .await
6430            .unwrap();
6431
6432        let fake_server = fake_servers.next().await.unwrap();
6433        fake_server.handle_request::<lsp::request::GotoDefinition, _, _>(|params, _| async move {
6434            let params = params.text_document_position_params;
6435            assert_eq!(
6436                params.text_document.uri.to_file_path().unwrap(),
6437                Path::new("/dir/b.rs"),
6438            );
6439            assert_eq!(params.position, lsp::Position::new(0, 22));
6440
6441            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
6442                lsp::Location::new(
6443                    lsp::Url::from_file_path("/dir/a.rs").unwrap(),
6444                    lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
6445                ),
6446            )))
6447        });
6448
6449        let mut definitions = project
6450            .update(cx, |project, cx| project.definition(&buffer, 22, cx))
6451            .await
6452            .unwrap();
6453
6454        assert_eq!(definitions.len(), 1);
6455        let definition = definitions.pop().unwrap();
6456        cx.update(|cx| {
6457            let target_buffer = definition.buffer.read(cx);
6458            assert_eq!(
6459                target_buffer
6460                    .file()
6461                    .unwrap()
6462                    .as_local()
6463                    .unwrap()
6464                    .abs_path(cx),
6465                Path::new("/dir/a.rs"),
6466            );
6467            assert_eq!(definition.range.to_offset(target_buffer), 9..10);
6468            assert_eq!(
6469                list_worktrees(&project, cx),
6470                [("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
6471            );
6472
6473            drop(definition);
6474        });
6475        cx.read(|cx| {
6476            assert_eq!(list_worktrees(&project, cx), [("/dir/b.rs".as_ref(), true)]);
6477        });
6478
6479        fn list_worktrees<'a>(
6480            project: &'a ModelHandle<Project>,
6481            cx: &'a AppContext,
6482        ) -> Vec<(&'a Path, bool)> {
6483            project
6484                .read(cx)
6485                .worktrees(cx)
6486                .map(|worktree| {
6487                    let worktree = worktree.read(cx);
6488                    (
6489                        worktree.as_local().unwrap().abs_path().as_ref(),
6490                        worktree.is_visible(),
6491                    )
6492                })
6493                .collect::<Vec<_>>()
6494        }
6495    }
6496
6497    #[gpui::test(iterations = 10)]
6498    async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
6499        let mut language = Language::new(
6500            LanguageConfig {
6501                name: "TypeScript".into(),
6502                path_suffixes: vec!["ts".to_string()],
6503                ..Default::default()
6504            },
6505            None,
6506        );
6507        let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
6508
6509        let fs = FakeFs::new(cx.background());
6510        fs.insert_tree(
6511            "/dir",
6512            json!({
6513                "a.ts": "a",
6514            }),
6515        )
6516        .await;
6517
6518        let project = Project::test(fs, cx);
6519        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
6520
6521        let (tree, _) = project
6522            .update(cx, |project, cx| {
6523                project.find_or_create_local_worktree("/dir", true, cx)
6524            })
6525            .await
6526            .unwrap();
6527        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
6528        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
6529            .await;
6530
6531        let buffer = project
6532            .update(cx, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx))
6533            .await
6534            .unwrap();
6535
6536        let fake_server = fake_language_servers.next().await.unwrap();
6537
6538        // Language server returns code actions that contain commands, and not edits.
6539        let actions = project.update(cx, |project, cx| project.code_actions(&buffer, 0..0, cx));
6540        fake_server
6541            .handle_request::<lsp::request::CodeActionRequest, _, _>(|_, _| async move {
6542                Ok(Some(vec![
6543                    lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
6544                        title: "The code action".into(),
6545                        command: Some(lsp::Command {
6546                            title: "The command".into(),
6547                            command: "_the/command".into(),
6548                            arguments: Some(vec![json!("the-argument")]),
6549                        }),
6550                        ..Default::default()
6551                    }),
6552                    lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
6553                        title: "two".into(),
6554                        ..Default::default()
6555                    }),
6556                ]))
6557            })
6558            .next()
6559            .await;
6560
6561        let action = actions.await.unwrap()[0].clone();
6562        let apply = project.update(cx, |project, cx| {
6563            project.apply_code_action(buffer.clone(), action, true, cx)
6564        });
6565
6566        // Resolving the code action does not populate its edits. In absence of
6567        // edits, we must execute the given command.
6568        fake_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
6569            |action, _| async move { Ok(action) },
6570        );
6571
6572        // While executing the command, the language server sends the editor
6573        // a `workspaceEdit` request.
6574        fake_server
6575            .handle_request::<lsp::request::ExecuteCommand, _, _>({
6576                let fake = fake_server.clone();
6577                move |params, _| {
6578                    assert_eq!(params.command, "_the/command");
6579                    let fake = fake.clone();
6580                    async move {
6581                        fake.server
6582                            .request::<lsp::request::ApplyWorkspaceEdit>(
6583                                lsp::ApplyWorkspaceEditParams {
6584                                    label: None,
6585                                    edit: lsp::WorkspaceEdit {
6586                                        changes: Some(
6587                                            [(
6588                                                lsp::Url::from_file_path("/dir/a.ts").unwrap(),
6589                                                vec![lsp::TextEdit {
6590                                                    range: lsp::Range::new(
6591                                                        lsp::Position::new(0, 0),
6592                                                        lsp::Position::new(0, 0),
6593                                                    ),
6594                                                    new_text: "X".into(),
6595                                                }],
6596                                            )]
6597                                            .into_iter()
6598                                            .collect(),
6599                                        ),
6600                                        ..Default::default()
6601                                    },
6602                                },
6603                            )
6604                            .await
6605                            .unwrap();
6606                        Ok(Some(json!(null)))
6607                    }
6608                }
6609            })
6610            .next()
6611            .await;
6612
6613        // Applying the code action returns a project transaction containing the edits
6614        // sent by the language server in its `workspaceEdit` request.
6615        let transaction = apply.await.unwrap();
6616        assert!(transaction.0.contains_key(&buffer));
6617        buffer.update(cx, |buffer, cx| {
6618            assert_eq!(buffer.text(), "Xa");
6619            buffer.undo(cx);
6620            assert_eq!(buffer.text(), "a");
6621        });
6622    }
6623
6624    #[gpui::test]
6625    async fn test_save_file(cx: &mut gpui::TestAppContext) {
6626        let fs = FakeFs::new(cx.background());
6627        fs.insert_tree(
6628            "/dir",
6629            json!({
6630                "file1": "the old contents",
6631            }),
6632        )
6633        .await;
6634
6635        let project = Project::test(fs.clone(), cx);
6636        let worktree_id = project
6637            .update(cx, |p, cx| {
6638                p.find_or_create_local_worktree("/dir", true, cx)
6639            })
6640            .await
6641            .unwrap()
6642            .0
6643            .read_with(cx, |tree, _| tree.id());
6644
6645        let buffer = project
6646            .update(cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
6647            .await
6648            .unwrap();
6649        buffer
6650            .update(cx, |buffer, cx| {
6651                assert_eq!(buffer.text(), "the old contents");
6652                buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
6653                buffer.save(cx)
6654            })
6655            .await
6656            .unwrap();
6657
6658        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
6659        assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
6660    }
6661
6662    #[gpui::test]
6663    async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
6664        let fs = FakeFs::new(cx.background());
6665        fs.insert_tree(
6666            "/dir",
6667            json!({
6668                "file1": "the old contents",
6669            }),
6670        )
6671        .await;
6672
6673        let project = Project::test(fs.clone(), cx);
6674        let worktree_id = project
6675            .update(cx, |p, cx| {
6676                p.find_or_create_local_worktree("/dir/file1", true, cx)
6677            })
6678            .await
6679            .unwrap()
6680            .0
6681            .read_with(cx, |tree, _| tree.id());
6682
6683        let buffer = project
6684            .update(cx, |p, cx| p.open_buffer((worktree_id, ""), cx))
6685            .await
6686            .unwrap();
6687        buffer
6688            .update(cx, |buffer, cx| {
6689                buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
6690                buffer.save(cx)
6691            })
6692            .await
6693            .unwrap();
6694
6695        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
6696        assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
6697    }
6698
6699    #[gpui::test]
6700    async fn test_save_as(cx: &mut gpui::TestAppContext) {
6701        let fs = FakeFs::new(cx.background());
6702        fs.insert_tree("/dir", json!({})).await;
6703
6704        let project = Project::test(fs.clone(), cx);
6705        let (worktree, _) = project
6706            .update(cx, |project, cx| {
6707                project.find_or_create_local_worktree("/dir", true, cx)
6708            })
6709            .await
6710            .unwrap();
6711        let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id());
6712
6713        let buffer = project.update(cx, |project, cx| {
6714            project.create_buffer("", None, cx).unwrap()
6715        });
6716        buffer.update(cx, |buffer, cx| {
6717            buffer.edit([0..0], "abc", cx);
6718            assert!(buffer.is_dirty());
6719            assert!(!buffer.has_conflict());
6720        });
6721        project
6722            .update(cx, |project, cx| {
6723                project.save_buffer_as(buffer.clone(), "/dir/file1".into(), cx)
6724            })
6725            .await
6726            .unwrap();
6727        assert_eq!(fs.load(Path::new("/dir/file1")).await.unwrap(), "abc");
6728        buffer.read_with(cx, |buffer, cx| {
6729            assert_eq!(buffer.file().unwrap().full_path(cx), Path::new("dir/file1"));
6730            assert!(!buffer.is_dirty());
6731            assert!(!buffer.has_conflict());
6732        });
6733
6734        let opened_buffer = project
6735            .update(cx, |project, cx| {
6736                project.open_buffer((worktree_id, "file1"), cx)
6737            })
6738            .await
6739            .unwrap();
6740        assert_eq!(opened_buffer, buffer);
6741    }
6742
6743    #[gpui::test(retries = 5)]
6744    async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
6745        let dir = temp_tree(json!({
6746            "a": {
6747                "file1": "",
6748                "file2": "",
6749                "file3": "",
6750            },
6751            "b": {
6752                "c": {
6753                    "file4": "",
6754                    "file5": "",
6755                }
6756            }
6757        }));
6758
6759        let project = Project::test(Arc::new(RealFs), cx);
6760        let rpc = project.read_with(cx, |p, _| p.client.clone());
6761
6762        let (tree, _) = project
6763            .update(cx, |p, cx| {
6764                p.find_or_create_local_worktree(dir.path(), true, cx)
6765            })
6766            .await
6767            .unwrap();
6768        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
6769
6770        let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| {
6771            let buffer = project.update(cx, |p, cx| p.open_buffer((worktree_id, path), cx));
6772            async move { buffer.await.unwrap() }
6773        };
6774        let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
6775            tree.read_with(cx, |tree, _| {
6776                tree.entry_for_path(path)
6777                    .expect(&format!("no entry for path {}", path))
6778                    .id
6779            })
6780        };
6781
6782        let buffer2 = buffer_for_path("a/file2", cx).await;
6783        let buffer3 = buffer_for_path("a/file3", cx).await;
6784        let buffer4 = buffer_for_path("b/c/file4", cx).await;
6785        let buffer5 = buffer_for_path("b/c/file5", cx).await;
6786
6787        let file2_id = id_for_path("a/file2", &cx);
6788        let file3_id = id_for_path("a/file3", &cx);
6789        let file4_id = id_for_path("b/c/file4", &cx);
6790
6791        // Wait for the initial scan.
6792        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
6793            .await;
6794
6795        // Create a remote copy of this worktree.
6796        let initial_snapshot = tree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
6797        let (remote, load_task) = cx.update(|cx| {
6798            Worktree::remote(
6799                1,
6800                1,
6801                initial_snapshot.to_proto(&Default::default(), true),
6802                rpc.clone(),
6803                cx,
6804            )
6805        });
6806        load_task.await;
6807
6808        cx.read(|cx| {
6809            assert!(!buffer2.read(cx).is_dirty());
6810            assert!(!buffer3.read(cx).is_dirty());
6811            assert!(!buffer4.read(cx).is_dirty());
6812            assert!(!buffer5.read(cx).is_dirty());
6813        });
6814
6815        // Rename and delete files and directories.
6816        tree.flush_fs_events(&cx).await;
6817        std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
6818        std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
6819        std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
6820        std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
6821        tree.flush_fs_events(&cx).await;
6822
6823        let expected_paths = vec![
6824            "a",
6825            "a/file1",
6826            "a/file2.new",
6827            "b",
6828            "d",
6829            "d/file3",
6830            "d/file4",
6831        ];
6832
6833        cx.read(|app| {
6834            assert_eq!(
6835                tree.read(app)
6836                    .paths()
6837                    .map(|p| p.to_str().unwrap())
6838                    .collect::<Vec<_>>(),
6839                expected_paths
6840            );
6841
6842            assert_eq!(id_for_path("a/file2.new", &cx), file2_id);
6843            assert_eq!(id_for_path("d/file3", &cx), file3_id);
6844            assert_eq!(id_for_path("d/file4", &cx), file4_id);
6845
6846            assert_eq!(
6847                buffer2.read(app).file().unwrap().path().as_ref(),
6848                Path::new("a/file2.new")
6849            );
6850            assert_eq!(
6851                buffer3.read(app).file().unwrap().path().as_ref(),
6852                Path::new("d/file3")
6853            );
6854            assert_eq!(
6855                buffer4.read(app).file().unwrap().path().as_ref(),
6856                Path::new("d/file4")
6857            );
6858            assert_eq!(
6859                buffer5.read(app).file().unwrap().path().as_ref(),
6860                Path::new("b/c/file5")
6861            );
6862
6863            assert!(!buffer2.read(app).file().unwrap().is_deleted());
6864            assert!(!buffer3.read(app).file().unwrap().is_deleted());
6865            assert!(!buffer4.read(app).file().unwrap().is_deleted());
6866            assert!(buffer5.read(app).file().unwrap().is_deleted());
6867        });
6868
6869        // Update the remote worktree. Check that it becomes consistent with the
6870        // local worktree.
6871        remote.update(cx, |remote, cx| {
6872            let update_message = tree.read(cx).as_local().unwrap().snapshot().build_update(
6873                &initial_snapshot,
6874                1,
6875                1,
6876                true,
6877            );
6878            remote
6879                .as_remote_mut()
6880                .unwrap()
6881                .snapshot
6882                .apply_remote_update(update_message)
6883                .unwrap();
6884
6885            assert_eq!(
6886                remote
6887                    .paths()
6888                    .map(|p| p.to_str().unwrap())
6889                    .collect::<Vec<_>>(),
6890                expected_paths
6891            );
6892        });
6893    }
6894
6895    #[gpui::test]
6896    async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
6897        let fs = FakeFs::new(cx.background());
6898        fs.insert_tree(
6899            "/the-dir",
6900            json!({
6901                "a.txt": "a-contents",
6902                "b.txt": "b-contents",
6903            }),
6904        )
6905        .await;
6906
6907        let project = Project::test(fs.clone(), cx);
6908        let worktree_id = project
6909            .update(cx, |p, cx| {
6910                p.find_or_create_local_worktree("/the-dir", true, cx)
6911            })
6912            .await
6913            .unwrap()
6914            .0
6915            .read_with(cx, |tree, _| tree.id());
6916
6917        // Spawn multiple tasks to open paths, repeating some paths.
6918        let (buffer_a_1, buffer_b, buffer_a_2) = project.update(cx, |p, cx| {
6919            (
6920                p.open_buffer((worktree_id, "a.txt"), cx),
6921                p.open_buffer((worktree_id, "b.txt"), cx),
6922                p.open_buffer((worktree_id, "a.txt"), cx),
6923            )
6924        });
6925
6926        let buffer_a_1 = buffer_a_1.await.unwrap();
6927        let buffer_a_2 = buffer_a_2.await.unwrap();
6928        let buffer_b = buffer_b.await.unwrap();
6929        assert_eq!(buffer_a_1.read_with(cx, |b, _| b.text()), "a-contents");
6930        assert_eq!(buffer_b.read_with(cx, |b, _| b.text()), "b-contents");
6931
6932        // There is only one buffer per path.
6933        let buffer_a_id = buffer_a_1.id();
6934        assert_eq!(buffer_a_2.id(), buffer_a_id);
6935
6936        // Open the same path again while it is still open.
6937        drop(buffer_a_1);
6938        let buffer_a_3 = project
6939            .update(cx, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
6940            .await
6941            .unwrap();
6942
6943        // There's still only one buffer per path.
6944        assert_eq!(buffer_a_3.id(), buffer_a_id);
6945    }
6946
6947    #[gpui::test]
6948    async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
6949        use std::fs;
6950
6951        let dir = temp_tree(json!({
6952            "file1": "abc",
6953            "file2": "def",
6954            "file3": "ghi",
6955        }));
6956
6957        let project = Project::test(Arc::new(RealFs), cx);
6958        let (worktree, _) = project
6959            .update(cx, |p, cx| {
6960                p.find_or_create_local_worktree(dir.path(), true, cx)
6961            })
6962            .await
6963            .unwrap();
6964        let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id());
6965
6966        worktree.flush_fs_events(&cx).await;
6967        worktree
6968            .read_with(cx, |t, _| t.as_local().unwrap().scan_complete())
6969            .await;
6970
6971        let buffer1 = project
6972            .update(cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
6973            .await
6974            .unwrap();
6975        let events = Rc::new(RefCell::new(Vec::new()));
6976
6977        // initially, the buffer isn't dirty.
6978        buffer1.update(cx, |buffer, cx| {
6979            cx.subscribe(&buffer1, {
6980                let events = events.clone();
6981                move |_, _, event, _| match event {
6982                    BufferEvent::Operation(_) => {}
6983                    _ => events.borrow_mut().push(event.clone()),
6984                }
6985            })
6986            .detach();
6987
6988            assert!(!buffer.is_dirty());
6989            assert!(events.borrow().is_empty());
6990
6991            buffer.edit(vec![1..2], "", cx);
6992        });
6993
6994        // after the first edit, the buffer is dirty, and emits a dirtied event.
6995        buffer1.update(cx, |buffer, cx| {
6996            assert!(buffer.text() == "ac");
6997            assert!(buffer.is_dirty());
6998            assert_eq!(
6999                *events.borrow(),
7000                &[language::Event::Edited, language::Event::Dirtied]
7001            );
7002            events.borrow_mut().clear();
7003            buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), None, cx);
7004        });
7005
7006        // after saving, the buffer is not dirty, and emits a saved event.
7007        buffer1.update(cx, |buffer, cx| {
7008            assert!(!buffer.is_dirty());
7009            assert_eq!(*events.borrow(), &[language::Event::Saved]);
7010            events.borrow_mut().clear();
7011
7012            buffer.edit(vec![1..1], "B", cx);
7013            buffer.edit(vec![2..2], "D", cx);
7014        });
7015
7016        // after editing again, the buffer is dirty, and emits another dirty event.
7017        buffer1.update(cx, |buffer, cx| {
7018            assert!(buffer.text() == "aBDc");
7019            assert!(buffer.is_dirty());
7020            assert_eq!(
7021                *events.borrow(),
7022                &[
7023                    language::Event::Edited,
7024                    language::Event::Dirtied,
7025                    language::Event::Edited,
7026                ],
7027            );
7028            events.borrow_mut().clear();
7029
7030            // TODO - currently, after restoring the buffer to its
7031            // previously-saved state, the is still considered dirty.
7032            buffer.edit([1..3], "", cx);
7033            assert!(buffer.text() == "ac");
7034            assert!(buffer.is_dirty());
7035        });
7036
7037        assert_eq!(*events.borrow(), &[language::Event::Edited]);
7038
7039        // When a file is deleted, the buffer is considered dirty.
7040        let events = Rc::new(RefCell::new(Vec::new()));
7041        let buffer2 = project
7042            .update(cx, |p, cx| p.open_buffer((worktree_id, "file2"), cx))
7043            .await
7044            .unwrap();
7045        buffer2.update(cx, |_, cx| {
7046            cx.subscribe(&buffer2, {
7047                let events = events.clone();
7048                move |_, _, event, _| events.borrow_mut().push(event.clone())
7049            })
7050            .detach();
7051        });
7052
7053        fs::remove_file(dir.path().join("file2")).unwrap();
7054        buffer2.condition(&cx, |b, _| b.is_dirty()).await;
7055        assert_eq!(
7056            *events.borrow(),
7057            &[language::Event::Dirtied, language::Event::FileHandleChanged]
7058        );
7059
7060        // When a file is already dirty when deleted, we don't emit a Dirtied event.
7061        let events = Rc::new(RefCell::new(Vec::new()));
7062        let buffer3 = project
7063            .update(cx, |p, cx| p.open_buffer((worktree_id, "file3"), cx))
7064            .await
7065            .unwrap();
7066        buffer3.update(cx, |_, cx| {
7067            cx.subscribe(&buffer3, {
7068                let events = events.clone();
7069                move |_, _, event, _| events.borrow_mut().push(event.clone())
7070            })
7071            .detach();
7072        });
7073
7074        worktree.flush_fs_events(&cx).await;
7075        buffer3.update(cx, |buffer, cx| {
7076            buffer.edit(Some(0..0), "x", cx);
7077        });
7078        events.borrow_mut().clear();
7079        fs::remove_file(dir.path().join("file3")).unwrap();
7080        buffer3
7081            .condition(&cx, |_, _| !events.borrow().is_empty())
7082            .await;
7083        assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
7084        cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
7085    }
7086
7087    #[gpui::test]
7088    async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
7089        use std::fs;
7090
7091        let initial_contents = "aaa\nbbbbb\nc\n";
7092        let dir = temp_tree(json!({ "the-file": initial_contents }));
7093
7094        let project = Project::test(Arc::new(RealFs), cx);
7095        let (worktree, _) = project
7096            .update(cx, |p, cx| {
7097                p.find_or_create_local_worktree(dir.path(), true, cx)
7098            })
7099            .await
7100            .unwrap();
7101        let worktree_id = worktree.read_with(cx, |tree, _| tree.id());
7102
7103        worktree
7104            .read_with(cx, |t, _| t.as_local().unwrap().scan_complete())
7105            .await;
7106
7107        let abs_path = dir.path().join("the-file");
7108        let buffer = project
7109            .update(cx, |p, cx| p.open_buffer((worktree_id, "the-file"), cx))
7110            .await
7111            .unwrap();
7112
7113        // TODO
7114        // Add a cursor on each row.
7115        // let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
7116        //     assert!(!buffer.is_dirty());
7117        //     buffer.add_selection_set(
7118        //         &(0..3)
7119        //             .map(|row| Selection {
7120        //                 id: row as usize,
7121        //                 start: Point::new(row, 1),
7122        //                 end: Point::new(row, 1),
7123        //                 reversed: false,
7124        //                 goal: SelectionGoal::None,
7125        //             })
7126        //             .collect::<Vec<_>>(),
7127        //         cx,
7128        //     )
7129        // });
7130
7131        // Change the file on disk, adding two new lines of text, and removing
7132        // one line.
7133        buffer.read_with(cx, |buffer, _| {
7134            assert!(!buffer.is_dirty());
7135            assert!(!buffer.has_conflict());
7136        });
7137        let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
7138        fs::write(&abs_path, new_contents).unwrap();
7139
7140        // Because the buffer was not modified, it is reloaded from disk. Its
7141        // contents are edited according to the diff between the old and new
7142        // file contents.
7143        buffer
7144            .condition(&cx, |buffer, _| buffer.text() == new_contents)
7145            .await;
7146
7147        buffer.update(cx, |buffer, _| {
7148            assert_eq!(buffer.text(), new_contents);
7149            assert!(!buffer.is_dirty());
7150            assert!(!buffer.has_conflict());
7151
7152            // TODO
7153            // let cursor_positions = buffer
7154            //     .selection_set(selection_set_id)
7155            //     .unwrap()
7156            //     .selections::<Point>(&*buffer)
7157            //     .map(|selection| {
7158            //         assert_eq!(selection.start, selection.end);
7159            //         selection.start
7160            //     })
7161            //     .collect::<Vec<_>>();
7162            // assert_eq!(
7163            //     cursor_positions,
7164            //     [Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
7165            // );
7166        });
7167
7168        // Modify the buffer
7169        buffer.update(cx, |buffer, cx| {
7170            buffer.edit(vec![0..0], " ", cx);
7171            assert!(buffer.is_dirty());
7172            assert!(!buffer.has_conflict());
7173        });
7174
7175        // Change the file on disk again, adding blank lines to the beginning.
7176        fs::write(&abs_path, "\n\n\nAAAA\naaa\nBB\nbbbbb\n").unwrap();
7177
7178        // Because the buffer is modified, it doesn't reload from disk, but is
7179        // marked as having a conflict.
7180        buffer
7181            .condition(&cx, |buffer, _| buffer.has_conflict())
7182            .await;
7183    }
7184
7185    #[gpui::test]
7186    async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
7187        cx.foreground().forbid_parking();
7188
7189        let fs = FakeFs::new(cx.background());
7190        fs.insert_tree(
7191            "/the-dir",
7192            json!({
7193                "a.rs": "
7194                    fn foo(mut v: Vec<usize>) {
7195                        for x in &v {
7196                            v.push(1);
7197                        }
7198                    }
7199                "
7200                .unindent(),
7201            }),
7202        )
7203        .await;
7204
7205        let project = Project::test(fs.clone(), cx);
7206        let (worktree, _) = project
7207            .update(cx, |p, cx| {
7208                p.find_or_create_local_worktree("/the-dir", true, cx)
7209            })
7210            .await
7211            .unwrap();
7212        let worktree_id = worktree.read_with(cx, |tree, _| tree.id());
7213
7214        let buffer = project
7215            .update(cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
7216            .await
7217            .unwrap();
7218
7219        let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
7220        let message = lsp::PublishDiagnosticsParams {
7221            uri: buffer_uri.clone(),
7222            diagnostics: vec![
7223                lsp::Diagnostic {
7224                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
7225                    severity: Some(DiagnosticSeverity::WARNING),
7226                    message: "error 1".to_string(),
7227                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
7228                        location: lsp::Location {
7229                            uri: buffer_uri.clone(),
7230                            range: lsp::Range::new(
7231                                lsp::Position::new(1, 8),
7232                                lsp::Position::new(1, 9),
7233                            ),
7234                        },
7235                        message: "error 1 hint 1".to_string(),
7236                    }]),
7237                    ..Default::default()
7238                },
7239                lsp::Diagnostic {
7240                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
7241                    severity: Some(DiagnosticSeverity::HINT),
7242                    message: "error 1 hint 1".to_string(),
7243                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
7244                        location: lsp::Location {
7245                            uri: buffer_uri.clone(),
7246                            range: lsp::Range::new(
7247                                lsp::Position::new(1, 8),
7248                                lsp::Position::new(1, 9),
7249                            ),
7250                        },
7251                        message: "original diagnostic".to_string(),
7252                    }]),
7253                    ..Default::default()
7254                },
7255                lsp::Diagnostic {
7256                    range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)),
7257                    severity: Some(DiagnosticSeverity::ERROR),
7258                    message: "error 2".to_string(),
7259                    related_information: Some(vec![
7260                        lsp::DiagnosticRelatedInformation {
7261                            location: lsp::Location {
7262                                uri: buffer_uri.clone(),
7263                                range: lsp::Range::new(
7264                                    lsp::Position::new(1, 13),
7265                                    lsp::Position::new(1, 15),
7266                                ),
7267                            },
7268                            message: "error 2 hint 1".to_string(),
7269                        },
7270                        lsp::DiagnosticRelatedInformation {
7271                            location: lsp::Location {
7272                                uri: buffer_uri.clone(),
7273                                range: lsp::Range::new(
7274                                    lsp::Position::new(1, 13),
7275                                    lsp::Position::new(1, 15),
7276                                ),
7277                            },
7278                            message: "error 2 hint 2".to_string(),
7279                        },
7280                    ]),
7281                    ..Default::default()
7282                },
7283                lsp::Diagnostic {
7284                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
7285                    severity: Some(DiagnosticSeverity::HINT),
7286                    message: "error 2 hint 1".to_string(),
7287                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
7288                        location: lsp::Location {
7289                            uri: buffer_uri.clone(),
7290                            range: lsp::Range::new(
7291                                lsp::Position::new(2, 8),
7292                                lsp::Position::new(2, 17),
7293                            ),
7294                        },
7295                        message: "original diagnostic".to_string(),
7296                    }]),
7297                    ..Default::default()
7298                },
7299                lsp::Diagnostic {
7300                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
7301                    severity: Some(DiagnosticSeverity::HINT),
7302                    message: "error 2 hint 2".to_string(),
7303                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
7304                        location: lsp::Location {
7305                            uri: buffer_uri.clone(),
7306                            range: lsp::Range::new(
7307                                lsp::Position::new(2, 8),
7308                                lsp::Position::new(2, 17),
7309                            ),
7310                        },
7311                        message: "original diagnostic".to_string(),
7312                    }]),
7313                    ..Default::default()
7314                },
7315            ],
7316            version: None,
7317        };
7318
7319        project
7320            .update(cx, |p, cx| p.update_diagnostics(message, &[], cx))
7321            .unwrap();
7322        let buffer = buffer.read_with(cx, |buffer, _| buffer.snapshot());
7323
7324        assert_eq!(
7325            buffer
7326                .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
7327                .collect::<Vec<_>>(),
7328            &[
7329                DiagnosticEntry {
7330                    range: Point::new(1, 8)..Point::new(1, 9),
7331                    diagnostic: Diagnostic {
7332                        severity: DiagnosticSeverity::WARNING,
7333                        message: "error 1".to_string(),
7334                        group_id: 0,
7335                        is_primary: true,
7336                        ..Default::default()
7337                    }
7338                },
7339                DiagnosticEntry {
7340                    range: Point::new(1, 8)..Point::new(1, 9),
7341                    diagnostic: Diagnostic {
7342                        severity: DiagnosticSeverity::HINT,
7343                        message: "error 1 hint 1".to_string(),
7344                        group_id: 0,
7345                        is_primary: false,
7346                        ..Default::default()
7347                    }
7348                },
7349                DiagnosticEntry {
7350                    range: Point::new(1, 13)..Point::new(1, 15),
7351                    diagnostic: Diagnostic {
7352                        severity: DiagnosticSeverity::HINT,
7353                        message: "error 2 hint 1".to_string(),
7354                        group_id: 1,
7355                        is_primary: false,
7356                        ..Default::default()
7357                    }
7358                },
7359                DiagnosticEntry {
7360                    range: Point::new(1, 13)..Point::new(1, 15),
7361                    diagnostic: Diagnostic {
7362                        severity: DiagnosticSeverity::HINT,
7363                        message: "error 2 hint 2".to_string(),
7364                        group_id: 1,
7365                        is_primary: false,
7366                        ..Default::default()
7367                    }
7368                },
7369                DiagnosticEntry {
7370                    range: Point::new(2, 8)..Point::new(2, 17),
7371                    diagnostic: Diagnostic {
7372                        severity: DiagnosticSeverity::ERROR,
7373                        message: "error 2".to_string(),
7374                        group_id: 1,
7375                        is_primary: true,
7376                        ..Default::default()
7377                    }
7378                }
7379            ]
7380        );
7381
7382        assert_eq!(
7383            buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
7384            &[
7385                DiagnosticEntry {
7386                    range: Point::new(1, 8)..Point::new(1, 9),
7387                    diagnostic: Diagnostic {
7388                        severity: DiagnosticSeverity::WARNING,
7389                        message: "error 1".to_string(),
7390                        group_id: 0,
7391                        is_primary: true,
7392                        ..Default::default()
7393                    }
7394                },
7395                DiagnosticEntry {
7396                    range: Point::new(1, 8)..Point::new(1, 9),
7397                    diagnostic: Diagnostic {
7398                        severity: DiagnosticSeverity::HINT,
7399                        message: "error 1 hint 1".to_string(),
7400                        group_id: 0,
7401                        is_primary: false,
7402                        ..Default::default()
7403                    }
7404                },
7405            ]
7406        );
7407        assert_eq!(
7408            buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
7409            &[
7410                DiagnosticEntry {
7411                    range: Point::new(1, 13)..Point::new(1, 15),
7412                    diagnostic: Diagnostic {
7413                        severity: DiagnosticSeverity::HINT,
7414                        message: "error 2 hint 1".to_string(),
7415                        group_id: 1,
7416                        is_primary: false,
7417                        ..Default::default()
7418                    }
7419                },
7420                DiagnosticEntry {
7421                    range: Point::new(1, 13)..Point::new(1, 15),
7422                    diagnostic: Diagnostic {
7423                        severity: DiagnosticSeverity::HINT,
7424                        message: "error 2 hint 2".to_string(),
7425                        group_id: 1,
7426                        is_primary: false,
7427                        ..Default::default()
7428                    }
7429                },
7430                DiagnosticEntry {
7431                    range: Point::new(2, 8)..Point::new(2, 17),
7432                    diagnostic: Diagnostic {
7433                        severity: DiagnosticSeverity::ERROR,
7434                        message: "error 2".to_string(),
7435                        group_id: 1,
7436                        is_primary: true,
7437                        ..Default::default()
7438                    }
7439                }
7440            ]
7441        );
7442    }
7443
7444    #[gpui::test]
7445    async fn test_rename(cx: &mut gpui::TestAppContext) {
7446        cx.foreground().forbid_parking();
7447
7448        let mut language = Language::new(
7449            LanguageConfig {
7450                name: "Rust".into(),
7451                path_suffixes: vec!["rs".to_string()],
7452                ..Default::default()
7453            },
7454            Some(tree_sitter_rust::language()),
7455        );
7456        let mut fake_servers = language.set_fake_lsp_adapter(Default::default());
7457
7458        let fs = FakeFs::new(cx.background());
7459        fs.insert_tree(
7460            "/dir",
7461            json!({
7462                "one.rs": "const ONE: usize = 1;",
7463                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
7464            }),
7465        )
7466        .await;
7467
7468        let project = Project::test(fs.clone(), cx);
7469        project.update(cx, |project, _| project.languages.add(Arc::new(language)));
7470
7471        let (tree, _) = project
7472            .update(cx, |project, cx| {
7473                project.find_or_create_local_worktree("/dir", true, cx)
7474            })
7475            .await
7476            .unwrap();
7477        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
7478        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
7479            .await;
7480
7481        let buffer = project
7482            .update(cx, |project, cx| {
7483                project.open_buffer((worktree_id, Path::new("one.rs")), cx)
7484            })
7485            .await
7486            .unwrap();
7487
7488        let fake_server = fake_servers.next().await.unwrap();
7489
7490        let response = project.update(cx, |project, cx| {
7491            project.prepare_rename(buffer.clone(), 7, cx)
7492        });
7493        fake_server
7494            .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
7495                assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
7496                assert_eq!(params.position, lsp::Position::new(0, 7));
7497                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
7498                    lsp::Position::new(0, 6),
7499                    lsp::Position::new(0, 9),
7500                ))))
7501            })
7502            .next()
7503            .await
7504            .unwrap();
7505        let range = response.await.unwrap().unwrap();
7506        let range = buffer.read_with(cx, |buffer, _| range.to_offset(buffer));
7507        assert_eq!(range, 6..9);
7508
7509        let response = project.update(cx, |project, cx| {
7510            project.perform_rename(buffer.clone(), 7, "THREE".to_string(), true, cx)
7511        });
7512        fake_server
7513            .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
7514                assert_eq!(
7515                    params.text_document_position.text_document.uri.as_str(),
7516                    "file:///dir/one.rs"
7517                );
7518                assert_eq!(
7519                    params.text_document_position.position,
7520                    lsp::Position::new(0, 7)
7521                );
7522                assert_eq!(params.new_name, "THREE");
7523                Ok(Some(lsp::WorkspaceEdit {
7524                    changes: Some(
7525                        [
7526                            (
7527                                lsp::Url::from_file_path("/dir/one.rs").unwrap(),
7528                                vec![lsp::TextEdit::new(
7529                                    lsp::Range::new(
7530                                        lsp::Position::new(0, 6),
7531                                        lsp::Position::new(0, 9),
7532                                    ),
7533                                    "THREE".to_string(),
7534                                )],
7535                            ),
7536                            (
7537                                lsp::Url::from_file_path("/dir/two.rs").unwrap(),
7538                                vec![
7539                                    lsp::TextEdit::new(
7540                                        lsp::Range::new(
7541                                            lsp::Position::new(0, 24),
7542                                            lsp::Position::new(0, 27),
7543                                        ),
7544                                        "THREE".to_string(),
7545                                    ),
7546                                    lsp::TextEdit::new(
7547                                        lsp::Range::new(
7548                                            lsp::Position::new(0, 35),
7549                                            lsp::Position::new(0, 38),
7550                                        ),
7551                                        "THREE".to_string(),
7552                                    ),
7553                                ],
7554                            ),
7555                        ]
7556                        .into_iter()
7557                        .collect(),
7558                    ),
7559                    ..Default::default()
7560                }))
7561            })
7562            .next()
7563            .await
7564            .unwrap();
7565        let mut transaction = response.await.unwrap().0;
7566        assert_eq!(transaction.len(), 2);
7567        assert_eq!(
7568            transaction
7569                .remove_entry(&buffer)
7570                .unwrap()
7571                .0
7572                .read_with(cx, |buffer, _| buffer.text()),
7573            "const THREE: usize = 1;"
7574        );
7575        assert_eq!(
7576            transaction
7577                .into_keys()
7578                .next()
7579                .unwrap()
7580                .read_with(cx, |buffer, _| buffer.text()),
7581            "const TWO: usize = one::THREE + one::THREE;"
7582        );
7583    }
7584
7585    #[gpui::test]
7586    async fn test_search(cx: &mut gpui::TestAppContext) {
7587        let fs = FakeFs::new(cx.background());
7588        fs.insert_tree(
7589            "/dir",
7590            json!({
7591                "one.rs": "const ONE: usize = 1;",
7592                "two.rs": "const TWO: usize = one::ONE + one::ONE;",
7593                "three.rs": "const THREE: usize = one::ONE + two::TWO;",
7594                "four.rs": "const FOUR: usize = one::ONE + three::THREE;",
7595            }),
7596        )
7597        .await;
7598        let project = Project::test(fs.clone(), cx);
7599        let (tree, _) = project
7600            .update(cx, |project, cx| {
7601                project.find_or_create_local_worktree("/dir", true, cx)
7602            })
7603            .await
7604            .unwrap();
7605        let worktree_id = tree.read_with(cx, |tree, _| tree.id());
7606        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
7607            .await;
7608
7609        assert_eq!(
7610            search(&project, SearchQuery::text("TWO", false, true), cx)
7611                .await
7612                .unwrap(),
7613            HashMap::from_iter([
7614                ("two.rs".to_string(), vec![6..9]),
7615                ("three.rs".to_string(), vec![37..40])
7616            ])
7617        );
7618
7619        let buffer_4 = project
7620            .update(cx, |project, cx| {
7621                project.open_buffer((worktree_id, "four.rs"), cx)
7622            })
7623            .await
7624            .unwrap();
7625        buffer_4.update(cx, |buffer, cx| {
7626            buffer.edit([20..28, 31..43], "two::TWO", cx);
7627        });
7628
7629        assert_eq!(
7630            search(&project, SearchQuery::text("TWO", false, true), cx)
7631                .await
7632                .unwrap(),
7633            HashMap::from_iter([
7634                ("two.rs".to_string(), vec![6..9]),
7635                ("three.rs".to_string(), vec![37..40]),
7636                ("four.rs".to_string(), vec![25..28, 36..39])
7637            ])
7638        );
7639
7640        async fn search(
7641            project: &ModelHandle<Project>,
7642            query: SearchQuery,
7643            cx: &mut gpui::TestAppContext,
7644        ) -> Result<HashMap<String, Vec<Range<usize>>>> {
7645            let results = project
7646                .update(cx, |project, cx| project.search(query, cx))
7647                .await?;
7648
7649            Ok(results
7650                .into_iter()
7651                .map(|(buffer, ranges)| {
7652                    buffer.read_with(cx, |buffer, _| {
7653                        let path = buffer.file().unwrap().path().to_string_lossy().to_string();
7654                        let ranges = ranges
7655                            .into_iter()
7656                            .map(|range| range.to_offset(buffer))
7657                            .collect::<Vec<_>>();
7658                        (path, ranges)
7659                    })
7660                })
7661                .collect())
7662        }
7663    }
7664}