project.rs

   1pub mod fs;
   2mod ignore;
   3pub mod worktree;
   4
   5use anyhow::{anyhow, Result};
   6use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
   7use clock::ReplicaId;
   8use collections::{hash_map, HashMap, HashSet};
   9use futures::Future;
  10use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
  11use gpui::{
  12    AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
  13    WeakModelHandle,
  14};
  15use language::{
  16    proto::{deserialize_anchor, serialize_anchor},
  17    range_from_lsp, Bias, Buffer, Diagnostic, DiagnosticEntry, File as _, Language,
  18    LanguageRegistry, Operation, PointUtf16, ToOffset, ToPointUtf16,
  19};
  20use lsp::{DiagnosticSeverity, LanguageServer};
  21use postage::{prelude::Stream, watch};
  22use smol::block_on;
  23use std::{
  24    convert::TryInto,
  25    ops::Range,
  26    path::{Path, PathBuf},
  27    sync::{atomic::AtomicBool, Arc},
  28};
  29use util::{post_inc, ResultExt, TryFutureExt as _};
  30
  31pub use fs::*;
  32pub use worktree::*;
  33
  34pub struct Project {
  35    worktrees: Vec<WorktreeHandle>,
  36    active_entry: Option<ProjectEntry>,
  37    languages: Arc<LanguageRegistry>,
  38    language_servers: HashMap<(WorktreeId, String), Arc<LanguageServer>>,
  39    client: Arc<client::Client>,
  40    user_store: ModelHandle<UserStore>,
  41    fs: Arc<dyn Fs>,
  42    client_state: ProjectClientState,
  43    collaborators: HashMap<PeerId, Collaborator>,
  44    subscriptions: Vec<client::Subscription>,
  45    language_servers_with_diagnostics_running: isize,
  46    open_buffers: HashMap<usize, OpenBuffer>,
  47    loading_buffers: HashMap<
  48        ProjectPath,
  49        postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
  50    >,
  51    shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
  52}
  53
  54enum OpenBuffer {
  55    Operations(Vec<Operation>),
  56    Loaded(WeakModelHandle<Buffer>),
  57}
  58
  59enum WorktreeHandle {
  60    Strong(ModelHandle<Worktree>),
  61    Weak(WeakModelHandle<Worktree>),
  62}
  63
  64enum ProjectClientState {
  65    Local {
  66        is_shared: bool,
  67        remote_id_tx: watch::Sender<Option<u64>>,
  68        remote_id_rx: watch::Receiver<Option<u64>>,
  69        _maintain_remote_id_task: Task<Option<()>>,
  70    },
  71    Remote {
  72        sharing_has_stopped: bool,
  73        remote_id: u64,
  74        replica_id: ReplicaId,
  75    },
  76}
  77
  78#[derive(Clone, Debug)]
  79pub struct Collaborator {
  80    pub user: Arc<User>,
  81    pub peer_id: PeerId,
  82    pub replica_id: ReplicaId,
  83}
  84
  85#[derive(Clone, Debug, PartialEq)]
  86pub enum Event {
  87    ActiveEntryChanged(Option<ProjectEntry>),
  88    WorktreeRemoved(WorktreeId),
  89    DiskBasedDiagnosticsStarted,
  90    DiskBasedDiagnosticsUpdated,
  91    DiskBasedDiagnosticsFinished,
  92    DiagnosticsUpdated(ProjectPath),
  93}
  94
  95#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
  96pub struct ProjectPath {
  97    pub worktree_id: WorktreeId,
  98    pub path: Arc<Path>,
  99}
 100
 101#[derive(Clone, Debug, Default, PartialEq)]
 102pub struct DiagnosticSummary {
 103    pub error_count: usize,
 104    pub warning_count: usize,
 105    pub info_count: usize,
 106    pub hint_count: usize,
 107}
 108
 109#[derive(Debug)]
 110pub struct Definition {
 111    pub target_buffer: ModelHandle<Buffer>,
 112    pub target_range: Range<language::Anchor>,
 113}
 114
 115impl DiagnosticSummary {
 116    fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
 117        let mut this = Self {
 118            error_count: 0,
 119            warning_count: 0,
 120            info_count: 0,
 121            hint_count: 0,
 122        };
 123
 124        for entry in diagnostics {
 125            if entry.diagnostic.is_primary {
 126                match entry.diagnostic.severity {
 127                    DiagnosticSeverity::ERROR => this.error_count += 1,
 128                    DiagnosticSeverity::WARNING => this.warning_count += 1,
 129                    DiagnosticSeverity::INFORMATION => this.info_count += 1,
 130                    DiagnosticSeverity::HINT => this.hint_count += 1,
 131                    _ => {}
 132                }
 133            }
 134        }
 135
 136        this
 137    }
 138
 139    pub fn to_proto(&self, path: Arc<Path>) -> proto::DiagnosticSummary {
 140        proto::DiagnosticSummary {
 141            path: path.to_string_lossy().to_string(),
 142            error_count: self.error_count as u32,
 143            warning_count: self.warning_count as u32,
 144            info_count: self.info_count as u32,
 145            hint_count: self.hint_count as u32,
 146        }
 147    }
 148}
 149
 150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 151pub struct ProjectEntry {
 152    pub worktree_id: WorktreeId,
 153    pub entry_id: usize,
 154}
 155
 156impl Project {
 157    pub fn local(
 158        client: Arc<Client>,
 159        user_store: ModelHandle<UserStore>,
 160        languages: Arc<LanguageRegistry>,
 161        fs: Arc<dyn Fs>,
 162        cx: &mut MutableAppContext,
 163    ) -> ModelHandle<Self> {
 164        cx.add_model(|cx: &mut ModelContext<Self>| {
 165            let (remote_id_tx, remote_id_rx) = watch::channel();
 166            let _maintain_remote_id_task = cx.spawn_weak({
 167                let rpc = client.clone();
 168                move |this, mut cx| {
 169                    async move {
 170                        let mut status = rpc.status();
 171                        while let Some(status) = status.recv().await {
 172                            if let Some(this) = this.upgrade(&cx) {
 173                                let remote_id = if let client::Status::Connected { .. } = status {
 174                                    let response = rpc.request(proto::RegisterProject {}).await?;
 175                                    Some(response.project_id)
 176                                } else {
 177                                    None
 178                                };
 179
 180                                if let Some(project_id) = remote_id {
 181                                    let mut registrations = Vec::new();
 182                                    this.update(&mut cx, |this, cx| {
 183                                        for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 184                                            registrations.push(worktree.update(
 185                                                cx,
 186                                                |worktree, cx| {
 187                                                    let worktree = worktree.as_local_mut().unwrap();
 188                                                    worktree.register(project_id, cx)
 189                                                },
 190                                            ));
 191                                        }
 192                                    });
 193                                    for registration in registrations {
 194                                        registration.await?;
 195                                    }
 196                                }
 197                                this.update(&mut cx, |this, cx| this.set_remote_id(remote_id, cx));
 198                            }
 199                        }
 200                        Ok(())
 201                    }
 202                    .log_err()
 203                }
 204            });
 205
 206            Self {
 207                worktrees: Default::default(),
 208                collaborators: Default::default(),
 209                open_buffers: Default::default(),
 210                loading_buffers: Default::default(),
 211                shared_buffers: Default::default(),
 212                client_state: ProjectClientState::Local {
 213                    is_shared: false,
 214                    remote_id_tx,
 215                    remote_id_rx,
 216                    _maintain_remote_id_task,
 217                },
 218                subscriptions: Vec::new(),
 219                active_entry: None,
 220                languages,
 221                client,
 222                user_store,
 223                fs,
 224                language_servers_with_diagnostics_running: 0,
 225                language_servers: Default::default(),
 226            }
 227        })
 228    }
 229
 230    pub async fn remote(
 231        remote_id: u64,
 232        client: Arc<Client>,
 233        user_store: ModelHandle<UserStore>,
 234        languages: Arc<LanguageRegistry>,
 235        fs: Arc<dyn Fs>,
 236        cx: &mut AsyncAppContext,
 237    ) -> Result<ModelHandle<Self>> {
 238        client.authenticate_and_connect(&cx).await?;
 239
 240        let response = client
 241            .request(proto::JoinProject {
 242                project_id: remote_id,
 243            })
 244            .await?;
 245
 246        let replica_id = response.replica_id as ReplicaId;
 247
 248        let mut worktrees = Vec::new();
 249        for worktree in response.worktrees {
 250            let (worktree, load_task) = cx
 251                .update(|cx| Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx));
 252            worktrees.push(worktree);
 253            load_task.detach();
 254        }
 255
 256        let user_ids = response
 257            .collaborators
 258            .iter()
 259            .map(|peer| peer.user_id)
 260            .collect();
 261        user_store
 262            .update(cx, |user_store, cx| user_store.load_users(user_ids, cx))
 263            .await?;
 264        let mut collaborators = HashMap::default();
 265        for message in response.collaborators {
 266            let collaborator = Collaborator::from_proto(message, &user_store, cx).await?;
 267            collaborators.insert(collaborator.peer_id, collaborator);
 268        }
 269
 270        Ok(cx.add_model(|cx| {
 271            let mut this = Self {
 272                worktrees: Vec::new(),
 273                open_buffers: Default::default(),
 274                loading_buffers: Default::default(),
 275                shared_buffers: Default::default(),
 276                active_entry: None,
 277                collaborators,
 278                languages,
 279                user_store,
 280                fs,
 281                subscriptions: vec![
 282                    client.subscribe_to_entity(remote_id, cx, Self::handle_unshare_project),
 283                    client.subscribe_to_entity(remote_id, cx, Self::handle_add_collaborator),
 284                    client.subscribe_to_entity(remote_id, cx, Self::handle_remove_collaborator),
 285                    client.subscribe_to_entity(remote_id, cx, Self::handle_share_worktree),
 286                    client.subscribe_to_entity(remote_id, cx, Self::handle_unregister_worktree),
 287                    client.subscribe_to_entity(remote_id, cx, Self::handle_update_worktree),
 288                    client.subscribe_to_entity(
 289                        remote_id,
 290                        cx,
 291                        Self::handle_update_diagnostic_summary,
 292                    ),
 293                    client.subscribe_to_entity(
 294                        remote_id,
 295                        cx,
 296                        Self::handle_disk_based_diagnostics_updating,
 297                    ),
 298                    client.subscribe_to_entity(
 299                        remote_id,
 300                        cx,
 301                        Self::handle_disk_based_diagnostics_updated,
 302                    ),
 303                    client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer),
 304                    client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer_file),
 305                    client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_reloaded),
 306                    client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
 307                ],
 308                client,
 309                client_state: ProjectClientState::Remote {
 310                    sharing_has_stopped: false,
 311                    remote_id,
 312                    replica_id,
 313                },
 314                language_servers_with_diagnostics_running: 0,
 315                language_servers: Default::default(),
 316            };
 317            for worktree in worktrees {
 318                this.add_worktree(&worktree, cx);
 319            }
 320            this
 321        }))
 322    }
 323
 324    fn set_remote_id(&mut self, remote_id: Option<u64>, cx: &mut ModelContext<Self>) {
 325        if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
 326            *remote_id_tx.borrow_mut() = remote_id;
 327        }
 328
 329        self.subscriptions.clear();
 330        if let Some(remote_id) = remote_id {
 331            let client = &self.client;
 332            self.subscriptions.extend([
 333                client.subscribe_to_entity(remote_id, cx, Self::handle_open_buffer),
 334                client.subscribe_to_entity(remote_id, cx, Self::handle_close_buffer),
 335                client.subscribe_to_entity(remote_id, cx, Self::handle_add_collaborator),
 336                client.subscribe_to_entity(remote_id, cx, Self::handle_remove_collaborator),
 337                client.subscribe_to_entity(remote_id, cx, Self::handle_update_worktree),
 338                client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer),
 339                client.subscribe_to_entity(remote_id, cx, Self::handle_save_buffer),
 340                client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
 341                client.subscribe_to_entity(remote_id, cx, Self::handle_format_buffer),
 342                client.subscribe_to_entity(remote_id, cx, Self::handle_get_definition),
 343            ]);
 344        }
 345    }
 346
 347    pub fn remote_id(&self) -> Option<u64> {
 348        match &self.client_state {
 349            ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
 350            ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
 351        }
 352    }
 353
 354    pub fn next_remote_id(&self) -> impl Future<Output = u64> {
 355        let mut id = None;
 356        let mut watch = None;
 357        match &self.client_state {
 358            ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
 359            ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
 360        }
 361
 362        async move {
 363            if let Some(id) = id {
 364                return id;
 365            }
 366            let mut watch = watch.unwrap();
 367            loop {
 368                let id = *watch.borrow();
 369                if let Some(id) = id {
 370                    return id;
 371                }
 372                watch.recv().await;
 373            }
 374        }
 375    }
 376
 377    pub fn replica_id(&self) -> ReplicaId {
 378        match &self.client_state {
 379            ProjectClientState::Local { .. } => 0,
 380            ProjectClientState::Remote { replica_id, .. } => *replica_id,
 381        }
 382    }
 383
 384    pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
 385        &self.collaborators
 386    }
 387
 388    pub fn worktrees<'a>(
 389        &'a self,
 390        cx: &'a AppContext,
 391    ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
 392        self.worktrees
 393            .iter()
 394            .filter_map(move |worktree| worktree.upgrade(cx))
 395    }
 396
 397    pub fn worktree_for_id(
 398        &self,
 399        id: WorktreeId,
 400        cx: &AppContext,
 401    ) -> Option<ModelHandle<Worktree>> {
 402        self.worktrees(cx)
 403            .find(|worktree| worktree.read(cx).id() == id)
 404    }
 405
 406    pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
 407        let rpc = self.client.clone();
 408        cx.spawn(|this, mut cx| async move {
 409            let project_id = this.update(&mut cx, |this, _| {
 410                if let ProjectClientState::Local {
 411                    is_shared,
 412                    remote_id_rx,
 413                    ..
 414                } = &mut this.client_state
 415                {
 416                    *is_shared = true;
 417                    remote_id_rx
 418                        .borrow()
 419                        .ok_or_else(|| anyhow!("no project id"))
 420                } else {
 421                    Err(anyhow!("can't share a remote project"))
 422                }
 423            })?;
 424
 425            rpc.request(proto::ShareProject { project_id }).await?;
 426            let mut tasks = Vec::new();
 427            this.update(&mut cx, |this, cx| {
 428                for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 429                    worktree.update(cx, |worktree, cx| {
 430                        let worktree = worktree.as_local_mut().unwrap();
 431                        tasks.push(worktree.share(project_id, cx));
 432                    });
 433                }
 434            });
 435            for task in tasks {
 436                task.await?;
 437            }
 438            this.update(&mut cx, |_, cx| cx.notify());
 439            Ok(())
 440        })
 441    }
 442
 443    pub fn unshare(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
 444        let rpc = self.client.clone();
 445        cx.spawn(|this, mut cx| async move {
 446            let project_id = this.update(&mut cx, |this, _| {
 447                if let ProjectClientState::Local {
 448                    is_shared,
 449                    remote_id_rx,
 450                    ..
 451                } = &mut this.client_state
 452                {
 453                    *is_shared = false;
 454                    remote_id_rx
 455                        .borrow()
 456                        .ok_or_else(|| anyhow!("no project id"))
 457                } else {
 458                    Err(anyhow!("can't share a remote project"))
 459                }
 460            })?;
 461
 462            rpc.send(proto::UnshareProject { project_id }).await?;
 463            this.update(&mut cx, |this, cx| {
 464                this.collaborators.clear();
 465                this.shared_buffers.clear();
 466                for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 467                    worktree.update(cx, |worktree, _| {
 468                        worktree.as_local_mut().unwrap().unshare();
 469                    });
 470                }
 471                cx.notify()
 472            });
 473            Ok(())
 474        })
 475    }
 476
 477    pub fn is_read_only(&self) -> bool {
 478        match &self.client_state {
 479            ProjectClientState::Local { .. } => false,
 480            ProjectClientState::Remote {
 481                sharing_has_stopped,
 482                ..
 483            } => *sharing_has_stopped,
 484        }
 485    }
 486
 487    pub fn is_local(&self) -> bool {
 488        match &self.client_state {
 489            ProjectClientState::Local { .. } => true,
 490            ProjectClientState::Remote { .. } => false,
 491        }
 492    }
 493
 494    pub fn open_buffer(
 495        &mut self,
 496        path: impl Into<ProjectPath>,
 497        cx: &mut ModelContext<Self>,
 498    ) -> Task<Result<ModelHandle<Buffer>>> {
 499        let project_path = path.into();
 500        let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
 501            worktree
 502        } else {
 503            return Task::ready(Err(anyhow!("no such worktree")));
 504        };
 505
 506        // If there is already a buffer for the given path, then return it.
 507        let existing_buffer = self.get_open_buffer(&project_path, cx);
 508        if let Some(existing_buffer) = existing_buffer {
 509            return Task::ready(Ok(existing_buffer));
 510        }
 511
 512        let mut loading_watch = match self.loading_buffers.entry(project_path.clone()) {
 513            // If the given path is already being loaded, then wait for that existing
 514            // task to complete and return the same buffer.
 515            hash_map::Entry::Occupied(e) => e.get().clone(),
 516
 517            // Otherwise, record the fact that this path is now being loaded.
 518            hash_map::Entry::Vacant(entry) => {
 519                let (mut tx, rx) = postage::watch::channel();
 520                entry.insert(rx.clone());
 521
 522                let load_buffer = if worktree.read(cx).is_local() {
 523                    self.open_local_buffer(&project_path.path, &worktree, cx)
 524                } else {
 525                    self.open_remote_buffer(&project_path.path, &worktree, cx)
 526                };
 527
 528                cx.spawn(move |this, mut cx| async move {
 529                    let load_result = load_buffer.await;
 530                    *tx.borrow_mut() = Some(this.update(&mut cx, |this, _| {
 531                        // Record the fact that the buffer is no longer loading.
 532                        this.loading_buffers.remove(&project_path);
 533                        let buffer = load_result.map_err(Arc::new)?;
 534                        Ok(buffer)
 535                    }));
 536                })
 537                .detach();
 538                rx
 539            }
 540        };
 541
 542        cx.foreground().spawn(async move {
 543            loop {
 544                if let Some(result) = loading_watch.borrow().as_ref() {
 545                    match result {
 546                        Ok(buffer) => return Ok(buffer.clone()),
 547                        Err(error) => return Err(anyhow!("{}", error)),
 548                    }
 549                }
 550                loading_watch.recv().await;
 551            }
 552        })
 553    }
 554
 555    fn open_local_buffer(
 556        &mut self,
 557        path: &Arc<Path>,
 558        worktree: &ModelHandle<Worktree>,
 559        cx: &mut ModelContext<Self>,
 560    ) -> Task<Result<ModelHandle<Buffer>>> {
 561        let load_buffer = worktree.update(cx, |worktree, cx| {
 562            let worktree = worktree.as_local_mut().unwrap();
 563            worktree.load_buffer(path, cx)
 564        });
 565        let worktree = worktree.downgrade();
 566        cx.spawn(|this, mut cx| async move {
 567            let buffer = load_buffer.await?;
 568            let worktree = worktree
 569                .upgrade(&cx)
 570                .ok_or_else(|| anyhow!("worktree was removed"))?;
 571            this.update(&mut cx, |this, cx| {
 572                this.register_buffer(&buffer, Some(&worktree), cx)
 573            })?;
 574            Ok(buffer)
 575        })
 576    }
 577
 578    fn open_remote_buffer(
 579        &mut self,
 580        path: &Arc<Path>,
 581        worktree: &ModelHandle<Worktree>,
 582        cx: &mut ModelContext<Self>,
 583    ) -> Task<Result<ModelHandle<Buffer>>> {
 584        let rpc = self.client.clone();
 585        let project_id = self.remote_id().unwrap();
 586        let remote_worktree_id = worktree.read(cx).id();
 587        let path = path.clone();
 588        let path_string = path.to_string_lossy().to_string();
 589        cx.spawn(|this, mut cx| async move {
 590            let response = rpc
 591                .request(proto::OpenBuffer {
 592                    project_id,
 593                    worktree_id: remote_worktree_id.to_proto(),
 594                    path: path_string,
 595                })
 596                .await?;
 597            let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
 598            this.update(&mut cx, |this, cx| {
 599                this.deserialize_remote_buffer(buffer, cx)
 600            })
 601        })
 602    }
 603
 604    pub fn save_buffer_as(
 605        &self,
 606        buffer: ModelHandle<Buffer>,
 607        abs_path: PathBuf,
 608        cx: &mut ModelContext<Project>,
 609    ) -> Task<Result<()>> {
 610        let worktree_task = self.find_or_create_local_worktree(&abs_path, false, cx);
 611        cx.spawn(|this, mut cx| async move {
 612            let (worktree, path) = worktree_task.await?;
 613            worktree
 614                .update(&mut cx, |worktree, cx| {
 615                    worktree
 616                        .as_local_mut()
 617                        .unwrap()
 618                        .save_buffer_as(buffer.clone(), path, cx)
 619                })
 620                .await?;
 621            this.update(&mut cx, |this, cx| {
 622                this.assign_language_to_buffer(&buffer, Some(&worktree), cx);
 623            });
 624            Ok(())
 625        })
 626    }
 627
 628    #[cfg(any(test, feature = "test-support"))]
 629    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
 630        let path = path.into();
 631        if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
 632            self.open_buffers.iter().any(|(_, buffer)| {
 633                if let Some(buffer) = buffer.upgrade(cx) {
 634                    if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
 635                        if file.worktree == worktree && file.path() == &path.path {
 636                            return true;
 637                        }
 638                    }
 639                }
 640                false
 641            })
 642        } else {
 643            false
 644        }
 645    }
 646
 647    fn get_open_buffer(
 648        &mut self,
 649        path: &ProjectPath,
 650        cx: &mut ModelContext<Self>,
 651    ) -> Option<ModelHandle<Buffer>> {
 652        let mut result = None;
 653        let worktree = self.worktree_for_id(path.worktree_id, cx)?;
 654        self.open_buffers.retain(|_, buffer| {
 655            if let OpenBuffer::Loaded(buffer) = buffer {
 656                if let Some(buffer) = buffer.upgrade(cx) {
 657                    if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
 658                        if file.worktree == worktree && file.path() == &path.path {
 659                            result = Some(buffer);
 660                        }
 661                    }
 662                    return true;
 663                }
 664            }
 665            false
 666        });
 667        result
 668    }
 669
 670    fn register_buffer(
 671        &mut self,
 672        buffer: &ModelHandle<Buffer>,
 673        worktree: Option<&ModelHandle<Worktree>>,
 674        cx: &mut ModelContext<Self>,
 675    ) -> Result<()> {
 676        match self.open_buffers.insert(
 677            buffer.read(cx).remote_id() as usize,
 678            OpenBuffer::Loaded(buffer.downgrade()),
 679        ) {
 680            Some(OpenBuffer::Operations(pending_ops)) => {
 681                // buffer.update(cx, |buf, cx| buf.apply_ops(pending_ops, cx))?;
 682            }
 683            Some(OpenBuffer::Loaded(_)) => {
 684                return Err(anyhow!("registered the same buffer twice"));
 685            }
 686            None => {}
 687        }
 688        self.assign_language_to_buffer(&buffer, worktree, cx);
 689        Ok(())
 690    }
 691
 692    fn assign_language_to_buffer(
 693        &mut self,
 694        buffer: &ModelHandle<Buffer>,
 695        worktree: Option<&ModelHandle<Worktree>>,
 696        cx: &mut ModelContext<Self>,
 697    ) -> Option<()> {
 698        let (path, full_path) = {
 699            let file = buffer.read(cx).file()?;
 700            (file.path().clone(), file.full_path(cx))
 701        };
 702
 703        // If the buffer has a language, set it and start/assign the language server
 704        if let Some(language) = self.languages.select_language(&full_path) {
 705            buffer.update(cx, |buffer, cx| {
 706                buffer.set_language(Some(language.clone()), cx);
 707            });
 708
 709            // For local worktrees, start a language server if needed.
 710            // Also assign the language server and any previously stored diagnostics to the buffer.
 711            if let Some(local_worktree) = worktree.and_then(|w| w.read(cx).as_local()) {
 712                let worktree_id = local_worktree.id();
 713                let worktree_abs_path = local_worktree.abs_path().clone();
 714
 715                let language_server = match self
 716                    .language_servers
 717                    .entry((worktree_id, language.name().to_string()))
 718                {
 719                    hash_map::Entry::Occupied(e) => Some(e.get().clone()),
 720                    hash_map::Entry::Vacant(e) => Self::start_language_server(
 721                        self.client.clone(),
 722                        language.clone(),
 723                        &worktree_abs_path,
 724                        cx,
 725                    )
 726                    .map(|server| e.insert(server).clone()),
 727                };
 728
 729                buffer.update(cx, |buffer, cx| {
 730                    buffer.set_language_server(language_server, cx);
 731                });
 732            }
 733        }
 734
 735        if let Some(local_worktree) = worktree.and_then(|w| w.read(cx).as_local()) {
 736            if let Some(diagnostics) = local_worktree.diagnostics_for_path(&path) {
 737                buffer.update(cx, |buffer, cx| {
 738                    buffer.update_diagnostics(None, diagnostics, cx).log_err();
 739                });
 740            }
 741        }
 742
 743        None
 744    }
 745
 746    fn start_language_server(
 747        rpc: Arc<Client>,
 748        language: Arc<Language>,
 749        worktree_path: &Path,
 750        cx: &mut ModelContext<Self>,
 751    ) -> Option<Arc<LanguageServer>> {
 752        enum LspEvent {
 753            DiagnosticsStart,
 754            DiagnosticsUpdate(lsp::PublishDiagnosticsParams),
 755            DiagnosticsFinish,
 756        }
 757
 758        let language_server = language
 759            .start_server(worktree_path, cx)
 760            .log_err()
 761            .flatten()?;
 762        let disk_based_sources = language
 763            .disk_based_diagnostic_sources()
 764            .cloned()
 765            .unwrap_or_default();
 766        let disk_based_diagnostics_progress_token =
 767            language.disk_based_diagnostics_progress_token().cloned();
 768        let has_disk_based_diagnostic_progress_token =
 769            disk_based_diagnostics_progress_token.is_some();
 770        let (diagnostics_tx, diagnostics_rx) = smol::channel::unbounded();
 771
 772        // Listen for `PublishDiagnostics` notifications.
 773        language_server
 774            .on_notification::<lsp::notification::PublishDiagnostics, _>({
 775                let diagnostics_tx = diagnostics_tx.clone();
 776                move |params| {
 777                    if !has_disk_based_diagnostic_progress_token {
 778                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 779                    }
 780                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsUpdate(params))).ok();
 781                    if !has_disk_based_diagnostic_progress_token {
 782                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 783                    }
 784                }
 785            })
 786            .detach();
 787
 788        // Listen for `Progress` notifications. Send an event when the language server
 789        // transitions between running jobs and not running any jobs.
 790        let mut running_jobs_for_this_server: i32 = 0;
 791        language_server
 792            .on_notification::<lsp::notification::Progress, _>(move |params| {
 793                let token = match params.token {
 794                    lsp::NumberOrString::Number(_) => None,
 795                    lsp::NumberOrString::String(token) => Some(token),
 796                };
 797
 798                if token == disk_based_diagnostics_progress_token {
 799                    match params.value {
 800                        lsp::ProgressParamsValue::WorkDone(progress) => match progress {
 801                            lsp::WorkDoneProgress::Begin(_) => {
 802                                running_jobs_for_this_server += 1;
 803                                if running_jobs_for_this_server == 1 {
 804                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 805                                }
 806                            }
 807                            lsp::WorkDoneProgress::End(_) => {
 808                                running_jobs_for_this_server -= 1;
 809                                if running_jobs_for_this_server == 0 {
 810                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 811                                }
 812                            }
 813                            _ => {}
 814                        },
 815                    }
 816                }
 817            })
 818            .detach();
 819
 820        // Process all the LSP events.
 821        cx.spawn_weak(|this, mut cx| async move {
 822            while let Ok(message) = diagnostics_rx.recv().await {
 823                let this = cx.read(|cx| this.upgrade(cx))?;
 824                match message {
 825                    LspEvent::DiagnosticsStart => {
 826                        let send = this.update(&mut cx, |this, cx| {
 827                            this.disk_based_diagnostics_started(cx);
 828                            this.remote_id().map(|project_id| {
 829                                rpc.send(proto::DiskBasedDiagnosticsUpdating { project_id })
 830                            })
 831                        });
 832                        if let Some(send) = send {
 833                            send.await.log_err();
 834                        }
 835                    }
 836                    LspEvent::DiagnosticsUpdate(params) => {
 837                        this.update(&mut cx, |this, cx| {
 838                            this.update_diagnostics(params, &disk_based_sources, cx)
 839                                .log_err();
 840                        });
 841                    }
 842                    LspEvent::DiagnosticsFinish => {
 843                        let send = this.update(&mut cx, |this, cx| {
 844                            this.disk_based_diagnostics_finished(cx);
 845                            this.remote_id().map(|project_id| {
 846                                rpc.send(proto::DiskBasedDiagnosticsUpdated { project_id })
 847                            })
 848                        });
 849                        if let Some(send) = send {
 850                            send.await.log_err();
 851                        }
 852                    }
 853                }
 854            }
 855            Some(())
 856        })
 857        .detach();
 858
 859        Some(language_server)
 860    }
 861
 862    pub fn update_diagnostics(
 863        &mut self,
 864        params: lsp::PublishDiagnosticsParams,
 865        disk_based_sources: &HashSet<String>,
 866        cx: &mut ModelContext<Self>,
 867    ) -> Result<()> {
 868        let abs_path = params
 869            .uri
 870            .to_file_path()
 871            .map_err(|_| anyhow!("URI is not a file"))?;
 872        let mut next_group_id = 0;
 873        let mut diagnostics = Vec::default();
 874        let mut primary_diagnostic_group_ids = HashMap::default();
 875        let mut sources_by_group_id = HashMap::default();
 876        let mut supporting_diagnostic_severities = HashMap::default();
 877        for diagnostic in &params.diagnostics {
 878            let source = diagnostic.source.as_ref();
 879            let code = diagnostic.code.as_ref().map(|code| match code {
 880                lsp::NumberOrString::Number(code) => code.to_string(),
 881                lsp::NumberOrString::String(code) => code.clone(),
 882            });
 883            let range = range_from_lsp(diagnostic.range);
 884            let is_supporting = diagnostic
 885                .related_information
 886                .as_ref()
 887                .map_or(false, |infos| {
 888                    infos.iter().any(|info| {
 889                        primary_diagnostic_group_ids.contains_key(&(
 890                            source,
 891                            code.clone(),
 892                            range_from_lsp(info.location.range),
 893                        ))
 894                    })
 895                });
 896
 897            if is_supporting {
 898                if let Some(severity) = diagnostic.severity {
 899                    supporting_diagnostic_severities
 900                        .insert((source, code.clone(), range), severity);
 901                }
 902            } else {
 903                let group_id = post_inc(&mut next_group_id);
 904                let is_disk_based =
 905                    source.map_or(false, |source| disk_based_sources.contains(source));
 906
 907                sources_by_group_id.insert(group_id, source);
 908                primary_diagnostic_group_ids
 909                    .insert((source, code.clone(), range.clone()), group_id);
 910
 911                diagnostics.push(DiagnosticEntry {
 912                    range,
 913                    diagnostic: Diagnostic {
 914                        code: code.clone(),
 915                        severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
 916                        message: diagnostic.message.clone(),
 917                        group_id,
 918                        is_primary: true,
 919                        is_valid: true,
 920                        is_disk_based,
 921                    },
 922                });
 923                if let Some(infos) = &diagnostic.related_information {
 924                    for info in infos {
 925                        if info.location.uri == params.uri {
 926                            let range = range_from_lsp(info.location.range);
 927                            diagnostics.push(DiagnosticEntry {
 928                                range,
 929                                diagnostic: Diagnostic {
 930                                    code: code.clone(),
 931                                    severity: DiagnosticSeverity::INFORMATION,
 932                                    message: info.message.clone(),
 933                                    group_id,
 934                                    is_primary: false,
 935                                    is_valid: true,
 936                                    is_disk_based,
 937                                },
 938                            });
 939                        }
 940                    }
 941                }
 942            }
 943        }
 944
 945        for entry in &mut diagnostics {
 946            let diagnostic = &mut entry.diagnostic;
 947            if !diagnostic.is_primary {
 948                let source = *sources_by_group_id.get(&diagnostic.group_id).unwrap();
 949                if let Some(&severity) = supporting_diagnostic_severities.get(&(
 950                    source,
 951                    diagnostic.code.clone(),
 952                    entry.range.clone(),
 953                )) {
 954                    diagnostic.severity = severity;
 955                }
 956            }
 957        }
 958
 959        self.update_diagnostic_entries(abs_path, params.version, diagnostics, cx)?;
 960        Ok(())
 961    }
 962
 963    pub fn update_diagnostic_entries(
 964        &mut self,
 965        abs_path: PathBuf,
 966        version: Option<i32>,
 967        diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
 968        cx: &mut ModelContext<Project>,
 969    ) -> Result<(), anyhow::Error> {
 970        let (worktree, relative_path) = self
 971            .find_local_worktree(&abs_path, cx)
 972            .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
 973        let project_path = ProjectPath {
 974            worktree_id: worktree.read(cx).id(),
 975            path: relative_path.into(),
 976        };
 977
 978        for buffer in self.open_buffers.values() {
 979            if let Some(buffer) = buffer.upgrade(cx) {
 980                if buffer
 981                    .read(cx)
 982                    .file()
 983                    .map_or(false, |file| *file.path() == project_path.path)
 984                {
 985                    buffer.update(cx, |buffer, cx| {
 986                        buffer.update_diagnostics(version, diagnostics.clone(), cx)
 987                    })?;
 988                    break;
 989                }
 990            }
 991        }
 992        worktree.update(cx, |worktree, cx| {
 993            worktree
 994                .as_local_mut()
 995                .ok_or_else(|| anyhow!("not a local worktree"))?
 996                .update_diagnostics(project_path.path.clone(), diagnostics, cx)
 997        })?;
 998        cx.emit(Event::DiagnosticsUpdated(project_path));
 999        Ok(())
1000    }
1001
1002    pub fn definition<T: ToOffset>(
1003        &self,
1004        source_buffer_handle: &ModelHandle<Buffer>,
1005        position: T,
1006        cx: &mut ModelContext<Self>,
1007    ) -> Task<Result<Vec<Definition>>> {
1008        let source_buffer_handle = source_buffer_handle.clone();
1009        let source_buffer = source_buffer_handle.read(cx);
1010        let worktree;
1011        let buffer_abs_path;
1012        if let Some(file) = File::from_dyn(source_buffer.file()) {
1013            worktree = file.worktree.clone();
1014            buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
1015        } else {
1016            return Task::ready(Err(anyhow!("buffer does not belong to any worktree")));
1017        };
1018
1019        if worktree.read(cx).as_local().is_some() {
1020            let point = source_buffer.offset_to_point_utf16(position.to_offset(source_buffer));
1021            let buffer_abs_path = buffer_abs_path.unwrap();
1022            let lang_name;
1023            let lang_server;
1024            if let Some(lang) = source_buffer.language() {
1025                lang_name = lang.name().to_string();
1026                if let Some(server) = self
1027                    .language_servers
1028                    .get(&(worktree.read(cx).id(), lang_name.clone()))
1029                {
1030                    lang_server = server.clone();
1031                } else {
1032                    return Task::ready(Err(anyhow!("buffer does not have a language server")));
1033                };
1034            } else {
1035                return Task::ready(Err(anyhow!("buffer does not have a language")));
1036            }
1037
1038            cx.spawn(|this, mut cx| async move {
1039                let response = lang_server
1040                    .request::<lsp::request::GotoDefinition>(lsp::GotoDefinitionParams {
1041                        text_document_position_params: lsp::TextDocumentPositionParams {
1042                            text_document: lsp::TextDocumentIdentifier::new(
1043                                lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
1044                            ),
1045                            position: lsp::Position::new(point.row, point.column),
1046                        },
1047                        work_done_progress_params: Default::default(),
1048                        partial_result_params: Default::default(),
1049                    })
1050                    .await?;
1051
1052                let mut definitions = Vec::new();
1053                if let Some(response) = response {
1054                    let mut unresolved_locations = Vec::new();
1055                    match response {
1056                        lsp::GotoDefinitionResponse::Scalar(loc) => {
1057                            unresolved_locations.push((loc.uri, loc.range));
1058                        }
1059                        lsp::GotoDefinitionResponse::Array(locs) => {
1060                            unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
1061                        }
1062                        lsp::GotoDefinitionResponse::Link(links) => {
1063                            unresolved_locations.extend(
1064                                links
1065                                    .into_iter()
1066                                    .map(|l| (l.target_uri, l.target_selection_range)),
1067                            );
1068                        }
1069                    }
1070
1071                    for (target_uri, target_range) in unresolved_locations {
1072                        let abs_path = target_uri
1073                            .to_file_path()
1074                            .map_err(|_| anyhow!("invalid target path"))?;
1075
1076                        let (worktree, relative_path) = if let Some(result) =
1077                            this.read_with(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
1078                        {
1079                            result
1080                        } else {
1081                            let worktree = this
1082                                .update(&mut cx, |this, cx| {
1083                                    this.create_local_worktree(&abs_path, true, cx)
1084                                })
1085                                .await?;
1086                            this.update(&mut cx, |this, cx| {
1087                                this.language_servers.insert(
1088                                    (worktree.read(cx).id(), lang_name.clone()),
1089                                    lang_server.clone(),
1090                                );
1091                            });
1092                            (worktree, PathBuf::new())
1093                        };
1094
1095                        let project_path = ProjectPath {
1096                            worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
1097                            path: relative_path.into(),
1098                        };
1099                        let target_buffer_handle = this
1100                            .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
1101                            .await?;
1102                        cx.read(|cx| {
1103                            let target_buffer = target_buffer_handle.read(cx);
1104                            let target_start = target_buffer
1105                                .clip_point_utf16(target_range.start.to_point_utf16(), Bias::Left);
1106                            let target_end = target_buffer
1107                                .clip_point_utf16(target_range.end.to_point_utf16(), Bias::Left);
1108                            definitions.push(Definition {
1109                                target_buffer: target_buffer_handle,
1110                                target_range: target_buffer.anchor_after(target_start)
1111                                    ..target_buffer.anchor_before(target_end),
1112                            });
1113                        });
1114                    }
1115                }
1116
1117                Ok(definitions)
1118            })
1119        } else if let Some(project_id) = self.remote_id() {
1120            let client = self.client.clone();
1121            let request = proto::GetDefinition {
1122                project_id,
1123                buffer_id: source_buffer.remote_id(),
1124                position: Some(serialize_anchor(&source_buffer.anchor_before(position))),
1125            };
1126            cx.spawn(|this, mut cx| async move {
1127                let response = client.request(request).await?;
1128                this.update(&mut cx, |this, cx| {
1129                    let mut definitions = Vec::new();
1130                    for definition in response.definitions {
1131                        let target_buffer = this.deserialize_remote_buffer(
1132                            definition.buffer.ok_or_else(|| anyhow!("missing buffer"))?,
1133                            cx,
1134                        )?;
1135                        let target_start = definition
1136                            .target_start
1137                            .and_then(deserialize_anchor)
1138                            .ok_or_else(|| anyhow!("missing target start"))?;
1139                        let target_end = definition
1140                            .target_end
1141                            .and_then(deserialize_anchor)
1142                            .ok_or_else(|| anyhow!("missing target end"))?;
1143                        definitions.push(Definition {
1144                            target_buffer,
1145                            target_range: target_start..target_end,
1146                        })
1147                    }
1148
1149                    Ok(definitions)
1150                })
1151            })
1152        } else {
1153            Task::ready(Err(anyhow!("project does not have a remote id")))
1154        }
1155    }
1156
1157    pub fn find_or_create_local_worktree(
1158        &self,
1159        abs_path: impl AsRef<Path>,
1160        weak: bool,
1161        cx: &mut ModelContext<Self>,
1162    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
1163        let abs_path = abs_path.as_ref();
1164        if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) {
1165            Task::ready(Ok((tree.clone(), relative_path.into())))
1166        } else {
1167            let worktree = self.create_local_worktree(abs_path, weak, cx);
1168            cx.foreground()
1169                .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
1170        }
1171    }
1172
1173    fn find_local_worktree(
1174        &self,
1175        abs_path: &Path,
1176        cx: &AppContext,
1177    ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
1178        for tree in self.worktrees(cx) {
1179            if let Some(relative_path) = tree
1180                .read(cx)
1181                .as_local()
1182                .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
1183            {
1184                return Some((tree.clone(), relative_path.into()));
1185            }
1186        }
1187        None
1188    }
1189
1190    pub fn is_shared(&self) -> bool {
1191        match &self.client_state {
1192            ProjectClientState::Local { is_shared, .. } => *is_shared,
1193            ProjectClientState::Remote { .. } => false,
1194        }
1195    }
1196
1197    fn create_local_worktree(
1198        &self,
1199        abs_path: impl AsRef<Path>,
1200        weak: bool,
1201        cx: &mut ModelContext<Self>,
1202    ) -> Task<Result<ModelHandle<Worktree>>> {
1203        let fs = self.fs.clone();
1204        let client = self.client.clone();
1205        let path = Arc::from(abs_path.as_ref());
1206        cx.spawn(|project, mut cx| async move {
1207            let worktree = Worktree::local(client.clone(), path, weak, fs, &mut cx).await?;
1208
1209            let (remote_project_id, is_shared) = project.update(&mut cx, |project, cx| {
1210                project.add_worktree(&worktree, cx);
1211                (project.remote_id(), project.is_shared())
1212            });
1213
1214            if let Some(project_id) = remote_project_id {
1215                worktree
1216                    .update(&mut cx, |worktree, cx| {
1217                        worktree.as_local_mut().unwrap().register(project_id, cx)
1218                    })
1219                    .await?;
1220                if is_shared {
1221                    worktree
1222                        .update(&mut cx, |worktree, cx| {
1223                            worktree.as_local_mut().unwrap().share(project_id, cx)
1224                        })
1225                        .await?;
1226                }
1227            }
1228
1229            Ok(worktree)
1230        })
1231    }
1232
1233    pub fn remove_worktree(&mut self, id: WorktreeId, cx: &mut ModelContext<Self>) {
1234        self.worktrees.retain(|worktree| {
1235            worktree
1236                .upgrade(cx)
1237                .map_or(false, |w| w.read(cx).id() != id)
1238        });
1239        cx.notify();
1240    }
1241
1242    fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
1243        cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
1244        if worktree.read(cx).is_local() {
1245            cx.subscribe(&worktree, |this, worktree, _, cx| {
1246                this.update_local_worktree_buffers(worktree, cx);
1247            })
1248            .detach();
1249        }
1250
1251        let push_weak_handle = {
1252            let worktree = worktree.read(cx);
1253            worktree.is_local() && worktree.is_weak()
1254        };
1255        if push_weak_handle {
1256            cx.observe_release(&worktree, |this, cx| {
1257                this.worktrees
1258                    .retain(|worktree| worktree.upgrade(cx).is_some());
1259                cx.notify();
1260            })
1261            .detach();
1262            self.worktrees
1263                .push(WorktreeHandle::Weak(worktree.downgrade()));
1264        } else {
1265            self.worktrees
1266                .push(WorktreeHandle::Strong(worktree.clone()));
1267        }
1268        cx.notify();
1269    }
1270
1271    fn update_local_worktree_buffers(
1272        &mut self,
1273        worktree_handle: ModelHandle<Worktree>,
1274        cx: &mut ModelContext<Self>,
1275    ) {
1276        let snapshot = worktree_handle.read(cx).snapshot();
1277        let mut buffers_to_delete = Vec::new();
1278        for (buffer_id, buffer) in &self.open_buffers {
1279            if let OpenBuffer::Loaded(buffer) = buffer {
1280                if let Some(buffer) = buffer.upgrade(cx) {
1281                    buffer.update(cx, |buffer, cx| {
1282                        if let Some(old_file) = File::from_dyn(buffer.file()) {
1283                            if old_file.worktree != worktree_handle {
1284                                return;
1285                            }
1286
1287                            let new_file = if let Some(entry) = old_file
1288                                .entry_id
1289                                .and_then(|entry_id| snapshot.entry_for_id(entry_id))
1290                            {
1291                                File {
1292                                    is_local: true,
1293                                    entry_id: Some(entry.id),
1294                                    mtime: entry.mtime,
1295                                    path: entry.path.clone(),
1296                                    worktree: worktree_handle.clone(),
1297                                }
1298                            } else if let Some(entry) =
1299                                snapshot.entry_for_path(old_file.path().as_ref())
1300                            {
1301                                File {
1302                                    is_local: true,
1303                                    entry_id: Some(entry.id),
1304                                    mtime: entry.mtime,
1305                                    path: entry.path.clone(),
1306                                    worktree: worktree_handle.clone(),
1307                                }
1308                            } else {
1309                                File {
1310                                    is_local: true,
1311                                    entry_id: None,
1312                                    path: old_file.path().clone(),
1313                                    mtime: old_file.mtime(),
1314                                    worktree: worktree_handle.clone(),
1315                                }
1316                            };
1317
1318                            if let Some(project_id) = self.remote_id() {
1319                                let client = self.client.clone();
1320                                let message = proto::UpdateBufferFile {
1321                                    project_id,
1322                                    buffer_id: *buffer_id as u64,
1323                                    file: Some(new_file.to_proto()),
1324                                };
1325                                cx.foreground()
1326                                    .spawn(async move { client.send(message).await })
1327                                    .detach_and_log_err(cx);
1328                            }
1329                            buffer.file_updated(Box::new(new_file), cx).detach();
1330                        }
1331                    });
1332                } else {
1333                    buffers_to_delete.push(*buffer_id);
1334                }
1335            }
1336        }
1337
1338        for buffer_id in buffers_to_delete {
1339            self.open_buffers.remove(&buffer_id);
1340        }
1341    }
1342
1343    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
1344        let new_active_entry = entry.and_then(|project_path| {
1345            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
1346            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
1347            Some(ProjectEntry {
1348                worktree_id: project_path.worktree_id,
1349                entry_id: entry.id,
1350            })
1351        });
1352        if new_active_entry != self.active_entry {
1353            self.active_entry = new_active_entry;
1354            cx.emit(Event::ActiveEntryChanged(new_active_entry));
1355        }
1356    }
1357
1358    pub fn is_running_disk_based_diagnostics(&self) -> bool {
1359        self.language_servers_with_diagnostics_running > 0
1360    }
1361
1362    pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
1363        let mut summary = DiagnosticSummary::default();
1364        for (_, path_summary) in self.diagnostic_summaries(cx) {
1365            summary.error_count += path_summary.error_count;
1366            summary.warning_count += path_summary.warning_count;
1367            summary.info_count += path_summary.info_count;
1368            summary.hint_count += path_summary.hint_count;
1369        }
1370        summary
1371    }
1372
1373    pub fn diagnostic_summaries<'a>(
1374        &'a self,
1375        cx: &'a AppContext,
1376    ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
1377        self.worktrees(cx).flat_map(move |worktree| {
1378            let worktree = worktree.read(cx);
1379            let worktree_id = worktree.id();
1380            worktree
1381                .diagnostic_summaries()
1382                .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
1383        })
1384    }
1385
1386    pub fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
1387        self.language_servers_with_diagnostics_running += 1;
1388        if self.language_servers_with_diagnostics_running == 1 {
1389            cx.emit(Event::DiskBasedDiagnosticsStarted);
1390        }
1391    }
1392
1393    pub fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
1394        cx.emit(Event::DiskBasedDiagnosticsUpdated);
1395        self.language_servers_with_diagnostics_running -= 1;
1396        if self.language_servers_with_diagnostics_running == 0 {
1397            cx.emit(Event::DiskBasedDiagnosticsFinished);
1398        }
1399    }
1400
1401    pub fn active_entry(&self) -> Option<ProjectEntry> {
1402        self.active_entry
1403    }
1404
1405    // RPC message handlers
1406
1407    fn handle_unshare_project(
1408        &mut self,
1409        _: TypedEnvelope<proto::UnshareProject>,
1410        _: Arc<Client>,
1411        cx: &mut ModelContext<Self>,
1412    ) -> Result<()> {
1413        if let ProjectClientState::Remote {
1414            sharing_has_stopped,
1415            ..
1416        } = &mut self.client_state
1417        {
1418            *sharing_has_stopped = true;
1419            self.collaborators.clear();
1420            cx.notify();
1421            Ok(())
1422        } else {
1423            unreachable!()
1424        }
1425    }
1426
1427    fn handle_add_collaborator(
1428        &mut self,
1429        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
1430        _: Arc<Client>,
1431        cx: &mut ModelContext<Self>,
1432    ) -> Result<()> {
1433        let user_store = self.user_store.clone();
1434        let collaborator = envelope
1435            .payload
1436            .collaborator
1437            .take()
1438            .ok_or_else(|| anyhow!("empty collaborator"))?;
1439
1440        cx.spawn(|this, mut cx| {
1441            async move {
1442                let collaborator =
1443                    Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
1444                this.update(&mut cx, |this, cx| {
1445                    this.collaborators
1446                        .insert(collaborator.peer_id, collaborator);
1447                    cx.notify();
1448                });
1449                Ok(())
1450            }
1451            .log_err()
1452        })
1453        .detach();
1454
1455        Ok(())
1456    }
1457
1458    fn handle_remove_collaborator(
1459        &mut self,
1460        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
1461        _: Arc<Client>,
1462        cx: &mut ModelContext<Self>,
1463    ) -> Result<()> {
1464        let peer_id = PeerId(envelope.payload.peer_id);
1465        let replica_id = self
1466            .collaborators
1467            .remove(&peer_id)
1468            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
1469            .replica_id;
1470        self.shared_buffers.remove(&peer_id);
1471        for (_, buffer) in &self.open_buffers {
1472            if let OpenBuffer::Loaded(buffer) = buffer {
1473                if let Some(buffer) = buffer.upgrade(cx) {
1474                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
1475                }
1476            }
1477        }
1478        cx.notify();
1479        Ok(())
1480    }
1481
1482    fn handle_share_worktree(
1483        &mut self,
1484        envelope: TypedEnvelope<proto::ShareWorktree>,
1485        client: Arc<Client>,
1486        cx: &mut ModelContext<Self>,
1487    ) -> Result<()> {
1488        let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
1489        let replica_id = self.replica_id();
1490        let worktree = envelope
1491            .payload
1492            .worktree
1493            .ok_or_else(|| anyhow!("invalid worktree"))?;
1494        let (worktree, load_task) = Worktree::remote(remote_id, replica_id, worktree, client, cx);
1495        self.add_worktree(&worktree, cx);
1496        load_task.detach();
1497        Ok(())
1498    }
1499
1500    fn handle_unregister_worktree(
1501        &mut self,
1502        envelope: TypedEnvelope<proto::UnregisterWorktree>,
1503        _: Arc<Client>,
1504        cx: &mut ModelContext<Self>,
1505    ) -> Result<()> {
1506        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1507        self.remove_worktree(worktree_id, cx);
1508        Ok(())
1509    }
1510
1511    fn handle_update_worktree(
1512        &mut self,
1513        envelope: TypedEnvelope<proto::UpdateWorktree>,
1514        _: Arc<Client>,
1515        cx: &mut ModelContext<Self>,
1516    ) -> Result<()> {
1517        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1518        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1519            worktree.update(cx, |worktree, cx| {
1520                let worktree = worktree.as_remote_mut().unwrap();
1521                worktree.update_from_remote(envelope, cx)
1522            })?;
1523        }
1524        Ok(())
1525    }
1526
1527    fn handle_update_diagnostic_summary(
1528        &mut self,
1529        envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
1530        _: Arc<Client>,
1531        cx: &mut ModelContext<Self>,
1532    ) -> Result<()> {
1533        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1534        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1535            if let Some(summary) = envelope.payload.summary {
1536                let project_path = ProjectPath {
1537                    worktree_id,
1538                    path: Path::new(&summary.path).into(),
1539                };
1540                worktree.update(cx, |worktree, _| {
1541                    worktree
1542                        .as_remote_mut()
1543                        .unwrap()
1544                        .update_diagnostic_summary(project_path.path.clone(), &summary);
1545                });
1546                cx.emit(Event::DiagnosticsUpdated(project_path));
1547            }
1548        }
1549        Ok(())
1550    }
1551
1552    fn handle_disk_based_diagnostics_updating(
1553        &mut self,
1554        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
1555        _: Arc<Client>,
1556        cx: &mut ModelContext<Self>,
1557    ) -> Result<()> {
1558        self.disk_based_diagnostics_started(cx);
1559        Ok(())
1560    }
1561
1562    fn handle_disk_based_diagnostics_updated(
1563        &mut self,
1564        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,
1565        _: Arc<Client>,
1566        cx: &mut ModelContext<Self>,
1567    ) -> Result<()> {
1568        self.disk_based_diagnostics_finished(cx);
1569        Ok(())
1570    }
1571
1572    pub fn handle_update_buffer(
1573        &mut self,
1574        envelope: TypedEnvelope<proto::UpdateBuffer>,
1575        _: Arc<Client>,
1576        cx: &mut ModelContext<Self>,
1577    ) -> Result<()> {
1578        let payload = envelope.payload.clone();
1579        let buffer_id = payload.buffer_id as usize;
1580        let ops = payload
1581            .operations
1582            .into_iter()
1583            .map(|op| language::proto::deserialize_operation(op))
1584            .collect::<Result<Vec<_>, _>>()?;
1585        match self.open_buffers.get_mut(&buffer_id) {
1586            Some(OpenBuffer::Operations(pending_ops)) => pending_ops.extend(ops),
1587            Some(OpenBuffer::Loaded(buffer)) => {
1588                if let Some(buffer) = buffer.upgrade(cx) {
1589                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
1590                } else {
1591                    self.open_buffers
1592                        .insert(buffer_id, OpenBuffer::Operations(ops));
1593                }
1594            }
1595            None => {
1596                self.open_buffers
1597                    .insert(buffer_id, OpenBuffer::Operations(ops));
1598            }
1599        }
1600        Ok(())
1601    }
1602
1603    pub fn handle_update_buffer_file(
1604        &mut self,
1605        envelope: TypedEnvelope<proto::UpdateBufferFile>,
1606        _: Arc<Client>,
1607        cx: &mut ModelContext<Self>,
1608    ) -> Result<()> {
1609        let payload = envelope.payload.clone();
1610        let buffer_id = payload.buffer_id as usize;
1611        let file = payload.file.ok_or_else(|| anyhow!("invalid file"))?;
1612        let worktree = self
1613            .worktree_for_id(WorktreeId::from_proto(file.worktree_id), cx)
1614            .ok_or_else(|| anyhow!("no such worktree"))?;
1615        let file = File::from_proto(file, worktree.clone(), cx)?;
1616        let buffer = self
1617            .open_buffers
1618            .get_mut(&buffer_id)
1619            .and_then(|b| b.upgrade(cx))
1620            .ok_or_else(|| anyhow!("no such buffer"))?;
1621        buffer.update(cx, |buffer, cx| {
1622            buffer.file_updated(Box::new(file), cx).detach();
1623        });
1624
1625        Ok(())
1626    }
1627
1628    pub fn handle_save_buffer(
1629        &mut self,
1630        envelope: TypedEnvelope<proto::SaveBuffer>,
1631        rpc: Arc<Client>,
1632        cx: &mut ModelContext<Self>,
1633    ) -> Result<()> {
1634        let sender_id = envelope.original_sender_id()?;
1635        let project_id = self.remote_id().ok_or_else(|| anyhow!("not connected"))?;
1636        let buffer = self
1637            .shared_buffers
1638            .get(&sender_id)
1639            .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
1640            .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
1641        let receipt = envelope.receipt();
1642        let buffer_id = envelope.payload.buffer_id;
1643        let save = cx.spawn(|_, mut cx| async move {
1644            buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await
1645        });
1646
1647        cx.background()
1648            .spawn(
1649                async move {
1650                    let (version, mtime) = save.await?;
1651
1652                    rpc.respond(
1653                        receipt,
1654                        proto::BufferSaved {
1655                            project_id,
1656                            buffer_id,
1657                            version: (&version).into(),
1658                            mtime: Some(mtime.into()),
1659                        },
1660                    )
1661                    .await?;
1662
1663                    Ok(())
1664                }
1665                .log_err(),
1666            )
1667            .detach();
1668        Ok(())
1669    }
1670
1671    pub fn handle_format_buffer(
1672        &mut self,
1673        envelope: TypedEnvelope<proto::FormatBuffer>,
1674        rpc: Arc<Client>,
1675        cx: &mut ModelContext<Self>,
1676    ) -> Result<()> {
1677        let receipt = envelope.receipt();
1678        let sender_id = envelope.original_sender_id()?;
1679        let buffer = self
1680            .shared_buffers
1681            .get(&sender_id)
1682            .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
1683            .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
1684        cx.spawn(|_, mut cx| async move {
1685            let format = buffer.update(&mut cx, |buffer, cx| buffer.format(cx)).await;
1686            // We spawn here in order to enqueue the sending of `Ack` *after* transmission of edits
1687            // associated with formatting.
1688            cx.spawn(|_| async move {
1689                match format {
1690                    Ok(()) => rpc.respond(receipt, proto::Ack {}).await?,
1691                    Err(error) => {
1692                        rpc.respond_with_error(
1693                            receipt,
1694                            proto::Error {
1695                                message: error.to_string(),
1696                            },
1697                        )
1698                        .await?
1699                    }
1700                }
1701                Ok::<_, anyhow::Error>(())
1702            })
1703            .await
1704            .log_err();
1705        })
1706        .detach();
1707        Ok(())
1708    }
1709
1710    pub fn handle_get_definition(
1711        &mut self,
1712        envelope: TypedEnvelope<proto::GetDefinition>,
1713        rpc: Arc<Client>,
1714        cx: &mut ModelContext<Self>,
1715    ) -> Result<()> {
1716        let receipt = envelope.receipt();
1717        let sender_id = envelope.original_sender_id()?;
1718        let source_buffer = self
1719            .shared_buffers
1720            .get(&sender_id)
1721            .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
1722            .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
1723        let position = envelope
1724            .payload
1725            .position
1726            .and_then(deserialize_anchor)
1727            .ok_or_else(|| anyhow!("invalid position"))?;
1728        if !source_buffer.read(cx).can_resolve(&position) {
1729            return Err(anyhow!("cannot resolve position"));
1730        }
1731
1732        let definitions = self.definition(&source_buffer, position, cx);
1733        cx.spawn(|this, mut cx| async move {
1734            let definitions = definitions.await?;
1735            let mut response = proto::GetDefinitionResponse {
1736                definitions: Default::default(),
1737            };
1738            this.update(&mut cx, |this, cx| {
1739                for definition in definitions {
1740                    let buffer =
1741                        this.serialize_buffer_for_peer(&definition.target_buffer, sender_id, cx);
1742                    response.definitions.push(proto::Definition {
1743                        target_start: Some(serialize_anchor(&definition.target_range.start)),
1744                        target_end: Some(serialize_anchor(&definition.target_range.end)),
1745                        buffer: Some(buffer),
1746                    });
1747                }
1748            });
1749            rpc.respond(receipt, response).await?;
1750            Ok::<_, anyhow::Error>(())
1751        })
1752        .detach_and_log_err(cx);
1753
1754        Ok(())
1755    }
1756
1757    pub fn handle_open_buffer(
1758        &mut self,
1759        envelope: TypedEnvelope<proto::OpenBuffer>,
1760        rpc: Arc<Client>,
1761        cx: &mut ModelContext<Self>,
1762    ) -> anyhow::Result<()> {
1763        let receipt = envelope.receipt();
1764        let peer_id = envelope.original_sender_id()?;
1765        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1766        let open_buffer = self.open_buffer(
1767            ProjectPath {
1768                worktree_id,
1769                path: PathBuf::from(envelope.payload.path).into(),
1770            },
1771            cx,
1772        );
1773        cx.spawn(|this, mut cx| {
1774            async move {
1775                let buffer = open_buffer.await?;
1776                let buffer = this.update(&mut cx, |this, cx| {
1777                    this.serialize_buffer_for_peer(&buffer, peer_id, cx)
1778                });
1779                rpc.respond(
1780                    receipt,
1781                    proto::OpenBufferResponse {
1782                        buffer: Some(buffer),
1783                    },
1784                )
1785                .await
1786            }
1787            .log_err()
1788        })
1789        .detach();
1790        Ok(())
1791    }
1792
1793    fn serialize_buffer_for_peer(
1794        &mut self,
1795        buffer: &ModelHandle<Buffer>,
1796        peer_id: PeerId,
1797        cx: &AppContext,
1798    ) -> proto::Buffer {
1799        let buffer_id = buffer.read(cx).remote_id();
1800        let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
1801        match shared_buffers.entry(buffer_id) {
1802            hash_map::Entry::Occupied(_) => proto::Buffer {
1803                variant: Some(proto::buffer::Variant::Id(buffer_id)),
1804            },
1805            hash_map::Entry::Vacant(entry) => {
1806                entry.insert(buffer.clone());
1807                proto::Buffer {
1808                    variant: Some(proto::buffer::Variant::State(buffer.read(cx).to_proto())),
1809                }
1810            }
1811        }
1812    }
1813
1814    fn deserialize_remote_buffer(
1815        &mut self,
1816        buffer: proto::Buffer,
1817        cx: &mut ModelContext<Self>,
1818    ) -> Result<ModelHandle<Buffer>> {
1819        match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
1820            proto::buffer::Variant::Id(id) => self
1821                .open_buffers
1822                .get(&(id as usize))
1823                .and_then(|buffer| buffer.upgrade(cx))
1824                .ok_or_else(|| anyhow!("no buffer exists for id {}", id)),
1825            proto::buffer::Variant::State(mut buffer) => {
1826                let mut buffer_worktree = None;
1827                let mut buffer_file = None;
1828                if let Some(file) = buffer.file.take() {
1829                    let worktree_id = WorktreeId::from_proto(file.worktree_id);
1830                    let worktree = self
1831                        .worktree_for_id(worktree_id, cx)
1832                        .ok_or_else(|| anyhow!("no worktree found for id {}", file.worktree_id))?;
1833                    buffer_file = Some(Box::new(File::from_proto(file, worktree.clone(), cx)?)
1834                        as Box<dyn language::File>);
1835                    buffer_worktree = Some(worktree);
1836                }
1837
1838                let buffer = cx.add_model(|cx| {
1839                    Buffer::from_proto(self.replica_id(), buffer, buffer_file, cx).unwrap()
1840                });
1841                self.register_buffer(&buffer, buffer_worktree.as_ref(), cx)?;
1842                Ok(buffer)
1843            }
1844        }
1845    }
1846
1847    pub fn handle_close_buffer(
1848        &mut self,
1849        envelope: TypedEnvelope<proto::CloseBuffer>,
1850        _: Arc<Client>,
1851        cx: &mut ModelContext<Self>,
1852    ) -> anyhow::Result<()> {
1853        if let Some(shared_buffers) = self.shared_buffers.get_mut(&envelope.original_sender_id()?) {
1854            shared_buffers.remove(&envelope.payload.buffer_id);
1855            cx.notify();
1856        }
1857        Ok(())
1858    }
1859
1860    pub fn handle_buffer_saved(
1861        &mut self,
1862        envelope: TypedEnvelope<proto::BufferSaved>,
1863        _: Arc<Client>,
1864        cx: &mut ModelContext<Self>,
1865    ) -> Result<()> {
1866        let payload = envelope.payload.clone();
1867        let buffer = self
1868            .open_buffers
1869            .get(&(payload.buffer_id as usize))
1870            .and_then(|buf| {
1871                if let OpenBuffer::Loaded(buffer) = buf {
1872                    buffer.upgrade(cx)
1873                } else {
1874                    None
1875                }
1876            });
1877        if let Some(buffer) = buffer {
1878            buffer.update(cx, |buffer, cx| {
1879                let version = payload.version.try_into()?;
1880                let mtime = payload
1881                    .mtime
1882                    .ok_or_else(|| anyhow!("missing mtime"))?
1883                    .into();
1884                buffer.did_save(version, mtime, None, cx);
1885                Result::<_, anyhow::Error>::Ok(())
1886            })?;
1887        }
1888        Ok(())
1889    }
1890
1891    pub fn handle_buffer_reloaded(
1892        &mut self,
1893        envelope: TypedEnvelope<proto::BufferReloaded>,
1894        _: Arc<Client>,
1895        cx: &mut ModelContext<Self>,
1896    ) -> Result<()> {
1897        let payload = envelope.payload.clone();
1898        let buffer = self
1899            .open_buffers
1900            .get(&(payload.buffer_id as usize))
1901            .and_then(|buf| {
1902                if let OpenBuffer::Loaded(buffer) = buf {
1903                    buffer.upgrade(cx)
1904                } else {
1905                    None
1906                }
1907            });
1908        if let Some(buffer) = buffer {
1909            buffer.update(cx, |buffer, cx| {
1910                let version = payload.version.try_into()?;
1911                let mtime = payload
1912                    .mtime
1913                    .ok_or_else(|| anyhow!("missing mtime"))?
1914                    .into();
1915                buffer.did_reload(version, mtime, cx);
1916                Result::<_, anyhow::Error>::Ok(())
1917            })?;
1918        }
1919        Ok(())
1920    }
1921
1922    pub fn match_paths<'a>(
1923        &self,
1924        query: &'a str,
1925        include_ignored: bool,
1926        smart_case: bool,
1927        max_results: usize,
1928        cancel_flag: &'a AtomicBool,
1929        cx: &AppContext,
1930    ) -> impl 'a + Future<Output = Vec<PathMatch>> {
1931        let worktrees = self
1932            .worktrees(cx)
1933            .filter(|worktree| !worktree.read(cx).is_weak())
1934            .collect::<Vec<_>>();
1935        let include_root_name = worktrees.len() > 1;
1936        let candidate_sets = worktrees
1937            .into_iter()
1938            .map(|worktree| CandidateSet {
1939                snapshot: worktree.read(cx).snapshot(),
1940                include_ignored,
1941                include_root_name,
1942            })
1943            .collect::<Vec<_>>();
1944
1945        let background = cx.background().clone();
1946        async move {
1947            fuzzy::match_paths(
1948                candidate_sets.as_slice(),
1949                query,
1950                smart_case,
1951                max_results,
1952                cancel_flag,
1953                background,
1954            )
1955            .await
1956        }
1957    }
1958}
1959
1960impl WorktreeHandle {
1961    pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
1962        match self {
1963            WorktreeHandle::Strong(handle) => Some(handle.clone()),
1964            WorktreeHandle::Weak(handle) => handle.upgrade(cx),
1965        }
1966    }
1967}
1968
1969struct CandidateSet {
1970    snapshot: Snapshot,
1971    include_ignored: bool,
1972    include_root_name: bool,
1973}
1974
1975impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
1976    type Candidates = CandidateSetIter<'a>;
1977
1978    fn id(&self) -> usize {
1979        self.snapshot.id().to_usize()
1980    }
1981
1982    fn len(&self) -> usize {
1983        if self.include_ignored {
1984            self.snapshot.file_count()
1985        } else {
1986            self.snapshot.visible_file_count()
1987        }
1988    }
1989
1990    fn prefix(&self) -> Arc<str> {
1991        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
1992            self.snapshot.root_name().into()
1993        } else if self.include_root_name {
1994            format!("{}/", self.snapshot.root_name()).into()
1995        } else {
1996            "".into()
1997        }
1998    }
1999
2000    fn candidates(&'a self, start: usize) -> Self::Candidates {
2001        CandidateSetIter {
2002            traversal: self.snapshot.files(self.include_ignored, start),
2003        }
2004    }
2005}
2006
2007struct CandidateSetIter<'a> {
2008    traversal: Traversal<'a>,
2009}
2010
2011impl<'a> Iterator for CandidateSetIter<'a> {
2012    type Item = PathMatchCandidate<'a>;
2013
2014    fn next(&mut self) -> Option<Self::Item> {
2015        self.traversal.next().map(|entry| {
2016            if let EntryKind::File(char_bag) = entry.kind {
2017                PathMatchCandidate {
2018                    path: &entry.path,
2019                    char_bag,
2020                }
2021            } else {
2022                unreachable!()
2023            }
2024        })
2025    }
2026}
2027
2028impl Entity for Project {
2029    type Event = Event;
2030
2031    fn release(&mut self, cx: &mut gpui::MutableAppContext) {
2032        match &self.client_state {
2033            ProjectClientState::Local { remote_id_rx, .. } => {
2034                if let Some(project_id) = *remote_id_rx.borrow() {
2035                    let rpc = self.client.clone();
2036                    cx.spawn(|_| async move {
2037                        if let Err(err) = rpc.send(proto::UnregisterProject { project_id }).await {
2038                            log::error!("error unregistering project: {}", err);
2039                        }
2040                    })
2041                    .detach();
2042                }
2043            }
2044            ProjectClientState::Remote { remote_id, .. } => {
2045                let rpc = self.client.clone();
2046                let project_id = *remote_id;
2047                cx.spawn(|_| async move {
2048                    if let Err(err) = rpc.send(proto::LeaveProject { project_id }).await {
2049                        log::error!("error leaving project: {}", err);
2050                    }
2051                })
2052                .detach();
2053            }
2054        }
2055    }
2056
2057    fn app_will_quit(
2058        &mut self,
2059        _: &mut MutableAppContext,
2060    ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
2061        use futures::FutureExt;
2062
2063        let shutdown_futures = self
2064            .language_servers
2065            .drain()
2066            .filter_map(|(_, server)| server.shutdown())
2067            .collect::<Vec<_>>();
2068        Some(
2069            async move {
2070                futures::future::join_all(shutdown_futures).await;
2071            }
2072            .boxed(),
2073        )
2074    }
2075}
2076
2077impl Collaborator {
2078    fn from_proto(
2079        message: proto::Collaborator,
2080        user_store: &ModelHandle<UserStore>,
2081        cx: &mut AsyncAppContext,
2082    ) -> impl Future<Output = Result<Self>> {
2083        let user = user_store.update(cx, |user_store, cx| {
2084            user_store.fetch_user(message.user_id, cx)
2085        });
2086
2087        async move {
2088            Ok(Self {
2089                peer_id: PeerId(message.peer_id),
2090                user: user.await?,
2091                replica_id: message.replica_id as ReplicaId,
2092            })
2093        }
2094    }
2095}
2096
2097impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
2098    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
2099        Self {
2100            worktree_id,
2101            path: path.as_ref().into(),
2102        }
2103    }
2104}
2105
2106impl OpenBuffer {
2107    fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
2108        match self {
2109            OpenBuffer::Loaded(buffer) => buffer.upgrade(cx),
2110            OpenBuffer::Operations(_) => None,
2111        }
2112    }
2113}
2114
2115#[cfg(test)]
2116mod tests {
2117    use super::{Event, *};
2118    use client::test::FakeHttpClient;
2119    use fs::RealFs;
2120    use futures::StreamExt;
2121    use gpui::{test::subscribe, TestAppContext};
2122    use language::{
2123        tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageRegistry,
2124        LanguageServerConfig, Point,
2125    };
2126    use lsp::Url;
2127    use serde_json::json;
2128    use std::{cell::RefCell, os::unix, path::PathBuf, rc::Rc};
2129    use unindent::Unindent as _;
2130    use util::test::temp_tree;
2131    use worktree::WorktreeHandle as _;
2132
2133    #[gpui::test]
2134    async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
2135        let dir = temp_tree(json!({
2136            "root": {
2137                "apple": "",
2138                "banana": {
2139                    "carrot": {
2140                        "date": "",
2141                        "endive": "",
2142                    }
2143                },
2144                "fennel": {
2145                    "grape": "",
2146                }
2147            }
2148        }));
2149
2150        let root_link_path = dir.path().join("root_link");
2151        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
2152        unix::fs::symlink(
2153            &dir.path().join("root/fennel"),
2154            &dir.path().join("root/finnochio"),
2155        )
2156        .unwrap();
2157
2158        let project = build_project(Arc::new(RealFs), &mut cx);
2159
2160        let (tree, _) = project
2161            .update(&mut cx, |project, cx| {
2162                project.find_or_create_local_worktree(&root_link_path, false, cx)
2163            })
2164            .await
2165            .unwrap();
2166
2167        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2168            .await;
2169        cx.read(|cx| {
2170            let tree = tree.read(cx);
2171            assert_eq!(tree.file_count(), 5);
2172            assert_eq!(
2173                tree.inode_for_path("fennel/grape"),
2174                tree.inode_for_path("finnochio/grape")
2175            );
2176        });
2177
2178        let cancel_flag = Default::default();
2179        let results = project
2180            .read_with(&cx, |project, cx| {
2181                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
2182            })
2183            .await;
2184        assert_eq!(
2185            results
2186                .into_iter()
2187                .map(|result| result.path)
2188                .collect::<Vec<Arc<Path>>>(),
2189            vec![
2190                PathBuf::from("banana/carrot/date").into(),
2191                PathBuf::from("banana/carrot/endive").into(),
2192            ]
2193        );
2194    }
2195
2196    #[gpui::test]
2197    async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
2198        let (language_server_config, mut fake_server) =
2199            LanguageServerConfig::fake(cx.background()).await;
2200        let progress_token = language_server_config
2201            .disk_based_diagnostics_progress_token
2202            .clone()
2203            .unwrap();
2204
2205        let mut languages = LanguageRegistry::new();
2206        languages.add(Arc::new(Language::new(
2207            LanguageConfig {
2208                name: "Rust".to_string(),
2209                path_suffixes: vec!["rs".to_string()],
2210                language_server: Some(language_server_config),
2211                ..Default::default()
2212            },
2213            Some(tree_sitter_rust::language()),
2214        )));
2215
2216        let dir = temp_tree(json!({
2217            "a.rs": "fn a() { A }",
2218            "b.rs": "const y: i32 = 1",
2219        }));
2220
2221        let http_client = FakeHttpClient::with_404_response();
2222        let client = Client::new(http_client.clone());
2223        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
2224
2225        let project = cx.update(|cx| {
2226            Project::local(
2227                client,
2228                user_store,
2229                Arc::new(languages),
2230                Arc::new(RealFs),
2231                cx,
2232            )
2233        });
2234
2235        let (tree, _) = project
2236            .update(&mut cx, |project, cx| {
2237                project.find_or_create_local_worktree(dir.path(), false, cx)
2238            })
2239            .await
2240            .unwrap();
2241        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
2242
2243        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2244            .await;
2245
2246        // Cause worktree to start the fake language server
2247        let _buffer = project
2248            .update(&mut cx, |project, cx| {
2249                project.open_buffer(
2250                    ProjectPath {
2251                        worktree_id,
2252                        path: Path::new("b.rs").into(),
2253                    },
2254                    cx,
2255                )
2256            })
2257            .await
2258            .unwrap();
2259
2260        let mut events = subscribe(&project, &mut cx);
2261
2262        fake_server.start_progress(&progress_token).await;
2263        assert_eq!(
2264            events.next().await.unwrap(),
2265            Event::DiskBasedDiagnosticsStarted
2266        );
2267
2268        fake_server.start_progress(&progress_token).await;
2269        fake_server.end_progress(&progress_token).await;
2270        fake_server.start_progress(&progress_token).await;
2271
2272        fake_server
2273            .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
2274                uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
2275                version: None,
2276                diagnostics: vec![lsp::Diagnostic {
2277                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
2278                    severity: Some(lsp::DiagnosticSeverity::ERROR),
2279                    message: "undefined variable 'A'".to_string(),
2280                    ..Default::default()
2281                }],
2282            })
2283            .await;
2284        assert_eq!(
2285            events.next().await.unwrap(),
2286            Event::DiagnosticsUpdated(ProjectPath {
2287                worktree_id,
2288                path: Arc::from(Path::new("a.rs"))
2289            })
2290        );
2291
2292        fake_server.end_progress(&progress_token).await;
2293        fake_server.end_progress(&progress_token).await;
2294        assert_eq!(
2295            events.next().await.unwrap(),
2296            Event::DiskBasedDiagnosticsUpdated
2297        );
2298        assert_eq!(
2299            events.next().await.unwrap(),
2300            Event::DiskBasedDiagnosticsFinished
2301        );
2302
2303        let buffer = project
2304            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
2305            .await
2306            .unwrap();
2307
2308        buffer.read_with(&cx, |buffer, _| {
2309            let snapshot = buffer.snapshot();
2310            let diagnostics = snapshot
2311                .diagnostics_in_range::<_, Point>(0..buffer.len())
2312                .collect::<Vec<_>>();
2313            assert_eq!(
2314                diagnostics,
2315                &[DiagnosticEntry {
2316                    range: Point::new(0, 9)..Point::new(0, 10),
2317                    diagnostic: Diagnostic {
2318                        severity: lsp::DiagnosticSeverity::ERROR,
2319                        message: "undefined variable 'A'".to_string(),
2320                        group_id: 0,
2321                        is_primary: true,
2322                        ..Default::default()
2323                    }
2324                }]
2325            )
2326        });
2327    }
2328
2329    #[gpui::test]
2330    async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
2331        let dir = temp_tree(json!({
2332            "root": {
2333                "dir1": {},
2334                "dir2": {
2335                    "dir3": {}
2336                }
2337            }
2338        }));
2339
2340        let project = build_project(Arc::new(RealFs), &mut cx);
2341        let (tree, _) = project
2342            .update(&mut cx, |project, cx| {
2343                project.find_or_create_local_worktree(&dir.path(), false, cx)
2344            })
2345            .await
2346            .unwrap();
2347
2348        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2349            .await;
2350
2351        let cancel_flag = Default::default();
2352        let results = project
2353            .read_with(&cx, |project, cx| {
2354                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
2355            })
2356            .await;
2357
2358        assert!(results.is_empty());
2359    }
2360
2361    #[gpui::test]
2362    async fn test_definition(mut cx: gpui::TestAppContext) {
2363        let (language_server_config, mut fake_server) =
2364            LanguageServerConfig::fake(cx.background()).await;
2365
2366        let mut languages = LanguageRegistry::new();
2367        languages.add(Arc::new(Language::new(
2368            LanguageConfig {
2369                name: "Rust".to_string(),
2370                path_suffixes: vec!["rs".to_string()],
2371                language_server: Some(language_server_config),
2372                ..Default::default()
2373            },
2374            Some(tree_sitter_rust::language()),
2375        )));
2376
2377        let dir = temp_tree(json!({
2378            "a.rs": "const fn a() { A }",
2379            "b.rs": "const y: i32 = crate::a()",
2380        }));
2381
2382        let http_client = FakeHttpClient::with_404_response();
2383        let client = Client::new(http_client.clone());
2384        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
2385        let project = cx.update(|cx| {
2386            Project::local(
2387                client,
2388                user_store,
2389                Arc::new(languages),
2390                Arc::new(RealFs),
2391                cx,
2392            )
2393        });
2394
2395        let (tree, _) = project
2396            .update(&mut cx, |project, cx| {
2397                project.find_or_create_local_worktree(dir.path().join("b.rs"), false, cx)
2398            })
2399            .await
2400            .unwrap();
2401        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
2402        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2403            .await;
2404
2405        // Cause worktree to start the fake language server
2406        let buffer = project
2407            .update(&mut cx, |project, cx| {
2408                project.open_buffer(
2409                    ProjectPath {
2410                        worktree_id,
2411                        path: Path::new("").into(),
2412                    },
2413                    cx,
2414                )
2415            })
2416            .await
2417            .unwrap();
2418        let definitions =
2419            project.update(&mut cx, |project, cx| project.definition(&buffer, 22, cx));
2420        let (request_id, request) = fake_server
2421            .receive_request::<lsp::request::GotoDefinition>()
2422            .await;
2423        let request_params = request.text_document_position_params;
2424        assert_eq!(
2425            request_params.text_document.uri.to_file_path().unwrap(),
2426            dir.path().join("b.rs")
2427        );
2428        assert_eq!(request_params.position, lsp::Position::new(0, 22));
2429
2430        fake_server
2431            .respond(
2432                request_id,
2433                Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new(
2434                    lsp::Url::from_file_path(dir.path().join("a.rs")).unwrap(),
2435                    lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
2436                ))),
2437            )
2438            .await;
2439        let mut definitions = definitions.await.unwrap();
2440        assert_eq!(definitions.len(), 1);
2441        let definition = definitions.pop().unwrap();
2442        cx.update(|cx| {
2443            let target_buffer = definition.target_buffer.read(cx);
2444            assert_eq!(
2445                target_buffer
2446                    .file()
2447                    .unwrap()
2448                    .as_local()
2449                    .unwrap()
2450                    .abs_path(cx),
2451                dir.path().join("a.rs")
2452            );
2453            assert_eq!(definition.target_range.to_offset(target_buffer), 9..10);
2454            assert_eq!(
2455                list_worktrees(&project, cx),
2456                [
2457                    (dir.path().join("b.rs"), false),
2458                    (dir.path().join("a.rs"), true)
2459                ]
2460            );
2461
2462            drop(definition);
2463        });
2464        cx.read(|cx| {
2465            assert_eq!(
2466                list_worktrees(&project, cx),
2467                [(dir.path().join("b.rs"), false)]
2468            );
2469        });
2470
2471        fn list_worktrees(project: &ModelHandle<Project>, cx: &AppContext) -> Vec<(PathBuf, bool)> {
2472            project
2473                .read(cx)
2474                .worktrees(cx)
2475                .map(|worktree| {
2476                    let worktree = worktree.read(cx);
2477                    (
2478                        worktree.as_local().unwrap().abs_path().to_path_buf(),
2479                        worktree.is_weak(),
2480                    )
2481                })
2482                .collect::<Vec<_>>()
2483        }
2484    }
2485
2486    #[gpui::test]
2487    async fn test_save_file(mut cx: gpui::TestAppContext) {
2488        let fs = Arc::new(FakeFs::new());
2489        fs.insert_tree(
2490            "/dir",
2491            json!({
2492                "file1": "the old contents",
2493            }),
2494        )
2495        .await;
2496
2497        let project = build_project(fs.clone(), &mut cx);
2498        let worktree_id = project
2499            .update(&mut cx, |p, cx| {
2500                p.find_or_create_local_worktree("/dir", false, cx)
2501            })
2502            .await
2503            .unwrap()
2504            .0
2505            .read_with(&cx, |tree, _| tree.id());
2506
2507        let buffer = project
2508            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
2509            .await
2510            .unwrap();
2511        buffer
2512            .update(&mut cx, |buffer, cx| {
2513                assert_eq!(buffer.text(), "the old contents");
2514                buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
2515                buffer.save(cx)
2516            })
2517            .await
2518            .unwrap();
2519
2520        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
2521        assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
2522    }
2523
2524    #[gpui::test]
2525    async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
2526        let fs = Arc::new(FakeFs::new());
2527        fs.insert_tree(
2528            "/dir",
2529            json!({
2530                "file1": "the old contents",
2531            }),
2532        )
2533        .await;
2534
2535        let project = build_project(fs.clone(), &mut cx);
2536        let worktree_id = project
2537            .update(&mut cx, |p, cx| {
2538                p.find_or_create_local_worktree("/dir/file1", false, cx)
2539            })
2540            .await
2541            .unwrap()
2542            .0
2543            .read_with(&cx, |tree, _| tree.id());
2544
2545        let buffer = project
2546            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, ""), cx))
2547            .await
2548            .unwrap();
2549        buffer
2550            .update(&mut cx, |buffer, cx| {
2551                buffer.edit(Some(0..0), "a line of text.\n".repeat(10 * 1024), cx);
2552                buffer.save(cx)
2553            })
2554            .await
2555            .unwrap();
2556
2557        let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
2558        assert_eq!(new_text, buffer.read_with(&cx, |buffer, _| buffer.text()));
2559    }
2560
2561    #[gpui::test]
2562    async fn test_rescan_and_remote_updates(mut cx: gpui::TestAppContext) {
2563        let dir = temp_tree(json!({
2564            "a": {
2565                "file1": "",
2566                "file2": "",
2567                "file3": "",
2568            },
2569            "b": {
2570                "c": {
2571                    "file4": "",
2572                    "file5": "",
2573                }
2574            }
2575        }));
2576
2577        let project = build_project(Arc::new(RealFs), &mut cx);
2578        let rpc = project.read_with(&cx, |p, _| p.client.clone());
2579
2580        let (tree, _) = project
2581            .update(&mut cx, |p, cx| {
2582                p.find_or_create_local_worktree(dir.path(), false, cx)
2583            })
2584            .await
2585            .unwrap();
2586        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
2587
2588        let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| {
2589            let buffer = project.update(cx, |p, cx| p.open_buffer((worktree_id, path), cx));
2590            async move { buffer.await.unwrap() }
2591        };
2592        let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
2593            tree.read_with(cx, |tree, _| {
2594                tree.entry_for_path(path)
2595                    .expect(&format!("no entry for path {}", path))
2596                    .id
2597            })
2598        };
2599
2600        let buffer2 = buffer_for_path("a/file2", &mut cx).await;
2601        let buffer3 = buffer_for_path("a/file3", &mut cx).await;
2602        let buffer4 = buffer_for_path("b/c/file4", &mut cx).await;
2603        let buffer5 = buffer_for_path("b/c/file5", &mut cx).await;
2604
2605        let file2_id = id_for_path("a/file2", &cx);
2606        let file3_id = id_for_path("a/file3", &cx);
2607        let file4_id = id_for_path("b/c/file4", &cx);
2608
2609        // Wait for the initial scan.
2610        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
2611            .await;
2612
2613        // Create a remote copy of this worktree.
2614        let initial_snapshot = tree.read_with(&cx, |tree, _| tree.snapshot());
2615        let (remote, load_task) = cx.update(|cx| {
2616            Worktree::remote(
2617                1,
2618                1,
2619                initial_snapshot.to_proto(&Default::default(), Default::default()),
2620                rpc.clone(),
2621                cx,
2622            )
2623        });
2624        load_task.await;
2625
2626        cx.read(|cx| {
2627            assert!(!buffer2.read(cx).is_dirty());
2628            assert!(!buffer3.read(cx).is_dirty());
2629            assert!(!buffer4.read(cx).is_dirty());
2630            assert!(!buffer5.read(cx).is_dirty());
2631        });
2632
2633        // Rename and delete files and directories.
2634        tree.flush_fs_events(&cx).await;
2635        std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
2636        std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
2637        std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
2638        std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
2639        tree.flush_fs_events(&cx).await;
2640
2641        let expected_paths = vec![
2642            "a",
2643            "a/file1",
2644            "a/file2.new",
2645            "b",
2646            "d",
2647            "d/file3",
2648            "d/file4",
2649        ];
2650
2651        cx.read(|app| {
2652            assert_eq!(
2653                tree.read(app)
2654                    .paths()
2655                    .map(|p| p.to_str().unwrap())
2656                    .collect::<Vec<_>>(),
2657                expected_paths
2658            );
2659
2660            assert_eq!(id_for_path("a/file2.new", &cx), file2_id);
2661            assert_eq!(id_for_path("d/file3", &cx), file3_id);
2662            assert_eq!(id_for_path("d/file4", &cx), file4_id);
2663
2664            assert_eq!(
2665                buffer2.read(app).file().unwrap().path().as_ref(),
2666                Path::new("a/file2.new")
2667            );
2668            assert_eq!(
2669                buffer3.read(app).file().unwrap().path().as_ref(),
2670                Path::new("d/file3")
2671            );
2672            assert_eq!(
2673                buffer4.read(app).file().unwrap().path().as_ref(),
2674                Path::new("d/file4")
2675            );
2676            assert_eq!(
2677                buffer5.read(app).file().unwrap().path().as_ref(),
2678                Path::new("b/c/file5")
2679            );
2680
2681            assert!(!buffer2.read(app).file().unwrap().is_deleted());
2682            assert!(!buffer3.read(app).file().unwrap().is_deleted());
2683            assert!(!buffer4.read(app).file().unwrap().is_deleted());
2684            assert!(buffer5.read(app).file().unwrap().is_deleted());
2685        });
2686
2687        // Update the remote worktree. Check that it becomes consistent with the
2688        // local worktree.
2689        remote.update(&mut cx, |remote, cx| {
2690            let update_message =
2691                tree.read(cx)
2692                    .snapshot()
2693                    .build_update(&initial_snapshot, 1, 1, true);
2694            remote
2695                .as_remote_mut()
2696                .unwrap()
2697                .snapshot
2698                .apply_remote_update(update_message)
2699                .unwrap();
2700
2701            assert_eq!(
2702                remote
2703                    .paths()
2704                    .map(|p| p.to_str().unwrap())
2705                    .collect::<Vec<_>>(),
2706                expected_paths
2707            );
2708        });
2709    }
2710
2711    #[gpui::test]
2712    async fn test_buffer_deduping(mut cx: gpui::TestAppContext) {
2713        let fs = Arc::new(FakeFs::new());
2714        fs.insert_tree(
2715            "/the-dir",
2716            json!({
2717                "a.txt": "a-contents",
2718                "b.txt": "b-contents",
2719            }),
2720        )
2721        .await;
2722
2723        let project = build_project(fs.clone(), &mut cx);
2724        let worktree_id = project
2725            .update(&mut cx, |p, cx| {
2726                p.find_or_create_local_worktree("/the-dir", false, cx)
2727            })
2728            .await
2729            .unwrap()
2730            .0
2731            .read_with(&cx, |tree, _| tree.id());
2732
2733        // Spawn multiple tasks to open paths, repeating some paths.
2734        let (buffer_a_1, buffer_b, buffer_a_2) = project.update(&mut cx, |p, cx| {
2735            (
2736                p.open_buffer((worktree_id, "a.txt"), cx),
2737                p.open_buffer((worktree_id, "b.txt"), cx),
2738                p.open_buffer((worktree_id, "a.txt"), cx),
2739            )
2740        });
2741
2742        let buffer_a_1 = buffer_a_1.await.unwrap();
2743        let buffer_a_2 = buffer_a_2.await.unwrap();
2744        let buffer_b = buffer_b.await.unwrap();
2745        assert_eq!(buffer_a_1.read_with(&cx, |b, _| b.text()), "a-contents");
2746        assert_eq!(buffer_b.read_with(&cx, |b, _| b.text()), "b-contents");
2747
2748        // There is only one buffer per path.
2749        let buffer_a_id = buffer_a_1.id();
2750        assert_eq!(buffer_a_2.id(), buffer_a_id);
2751
2752        // Open the same path again while it is still open.
2753        drop(buffer_a_1);
2754        let buffer_a_3 = project
2755            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
2756            .await
2757            .unwrap();
2758
2759        // There's still only one buffer per path.
2760        assert_eq!(buffer_a_3.id(), buffer_a_id);
2761    }
2762
2763    #[gpui::test]
2764    async fn test_buffer_is_dirty(mut cx: gpui::TestAppContext) {
2765        use std::fs;
2766
2767        let dir = temp_tree(json!({
2768            "file1": "abc",
2769            "file2": "def",
2770            "file3": "ghi",
2771        }));
2772
2773        let project = build_project(Arc::new(RealFs), &mut cx);
2774        let (worktree, _) = project
2775            .update(&mut cx, |p, cx| {
2776                p.find_or_create_local_worktree(dir.path(), false, cx)
2777            })
2778            .await
2779            .unwrap();
2780        let worktree_id = worktree.read_with(&cx, |worktree, _| worktree.id());
2781
2782        worktree.flush_fs_events(&cx).await;
2783        worktree
2784            .read_with(&cx, |t, _| t.as_local().unwrap().scan_complete())
2785            .await;
2786
2787        let buffer1 = project
2788            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
2789            .await
2790            .unwrap();
2791        let events = Rc::new(RefCell::new(Vec::new()));
2792
2793        // initially, the buffer isn't dirty.
2794        buffer1.update(&mut cx, |buffer, cx| {
2795            cx.subscribe(&buffer1, {
2796                let events = events.clone();
2797                move |_, _, event, _| events.borrow_mut().push(event.clone())
2798            })
2799            .detach();
2800
2801            assert!(!buffer.is_dirty());
2802            assert!(events.borrow().is_empty());
2803
2804            buffer.edit(vec![1..2], "", cx);
2805        });
2806
2807        // after the first edit, the buffer is dirty, and emits a dirtied event.
2808        buffer1.update(&mut cx, |buffer, cx| {
2809            assert!(buffer.text() == "ac");
2810            assert!(buffer.is_dirty());
2811            assert_eq!(
2812                *events.borrow(),
2813                &[language::Event::Edited, language::Event::Dirtied]
2814            );
2815            events.borrow_mut().clear();
2816            buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), None, cx);
2817        });
2818
2819        // after saving, the buffer is not dirty, and emits a saved event.
2820        buffer1.update(&mut cx, |buffer, cx| {
2821            assert!(!buffer.is_dirty());
2822            assert_eq!(*events.borrow(), &[language::Event::Saved]);
2823            events.borrow_mut().clear();
2824
2825            buffer.edit(vec![1..1], "B", cx);
2826            buffer.edit(vec![2..2], "D", cx);
2827        });
2828
2829        // after editing again, the buffer is dirty, and emits another dirty event.
2830        buffer1.update(&mut cx, |buffer, cx| {
2831            assert!(buffer.text() == "aBDc");
2832            assert!(buffer.is_dirty());
2833            assert_eq!(
2834                *events.borrow(),
2835                &[
2836                    language::Event::Edited,
2837                    language::Event::Dirtied,
2838                    language::Event::Edited,
2839                ],
2840            );
2841            events.borrow_mut().clear();
2842
2843            // TODO - currently, after restoring the buffer to its
2844            // previously-saved state, the is still considered dirty.
2845            buffer.edit([1..3], "", cx);
2846            assert!(buffer.text() == "ac");
2847            assert!(buffer.is_dirty());
2848        });
2849
2850        assert_eq!(*events.borrow(), &[language::Event::Edited]);
2851
2852        // When a file is deleted, the buffer is considered dirty.
2853        let events = Rc::new(RefCell::new(Vec::new()));
2854        let buffer2 = project
2855            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file2"), cx))
2856            .await
2857            .unwrap();
2858        buffer2.update(&mut cx, |_, cx| {
2859            cx.subscribe(&buffer2, {
2860                let events = events.clone();
2861                move |_, _, event, _| events.borrow_mut().push(event.clone())
2862            })
2863            .detach();
2864        });
2865
2866        fs::remove_file(dir.path().join("file2")).unwrap();
2867        buffer2.condition(&cx, |b, _| b.is_dirty()).await;
2868        assert_eq!(
2869            *events.borrow(),
2870            &[language::Event::Dirtied, language::Event::FileHandleChanged]
2871        );
2872
2873        // When a file is already dirty when deleted, we don't emit a Dirtied event.
2874        let events = Rc::new(RefCell::new(Vec::new()));
2875        let buffer3 = project
2876            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "file3"), cx))
2877            .await
2878            .unwrap();
2879        buffer3.update(&mut cx, |_, cx| {
2880            cx.subscribe(&buffer3, {
2881                let events = events.clone();
2882                move |_, _, event, _| events.borrow_mut().push(event.clone())
2883            })
2884            .detach();
2885        });
2886
2887        worktree.flush_fs_events(&cx).await;
2888        buffer3.update(&mut cx, |buffer, cx| {
2889            buffer.edit(Some(0..0), "x", cx);
2890        });
2891        events.borrow_mut().clear();
2892        fs::remove_file(dir.path().join("file3")).unwrap();
2893        buffer3
2894            .condition(&cx, |_, _| !events.borrow().is_empty())
2895            .await;
2896        assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
2897        cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
2898    }
2899
2900    #[gpui::test]
2901    async fn test_buffer_file_changes_on_disk(mut cx: gpui::TestAppContext) {
2902        use std::fs;
2903
2904        let initial_contents = "aaa\nbbbbb\nc\n";
2905        let dir = temp_tree(json!({ "the-file": initial_contents }));
2906
2907        let project = build_project(Arc::new(RealFs), &mut cx);
2908        let (worktree, _) = project
2909            .update(&mut cx, |p, cx| {
2910                p.find_or_create_local_worktree(dir.path(), false, cx)
2911            })
2912            .await
2913            .unwrap();
2914        let worktree_id = worktree.read_with(&cx, |tree, _| tree.id());
2915
2916        worktree
2917            .read_with(&cx, |t, _| t.as_local().unwrap().scan_complete())
2918            .await;
2919
2920        let abs_path = dir.path().join("the-file");
2921        let buffer = project
2922            .update(&mut cx, |p, cx| {
2923                p.open_buffer((worktree_id, "the-file"), cx)
2924            })
2925            .await
2926            .unwrap();
2927
2928        // TODO
2929        // Add a cursor on each row.
2930        // let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
2931        //     assert!(!buffer.is_dirty());
2932        //     buffer.add_selection_set(
2933        //         &(0..3)
2934        //             .map(|row| Selection {
2935        //                 id: row as usize,
2936        //                 start: Point::new(row, 1),
2937        //                 end: Point::new(row, 1),
2938        //                 reversed: false,
2939        //                 goal: SelectionGoal::None,
2940        //             })
2941        //             .collect::<Vec<_>>(),
2942        //         cx,
2943        //     )
2944        // });
2945
2946        // Change the file on disk, adding two new lines of text, and removing
2947        // one line.
2948        buffer.read_with(&cx, |buffer, _| {
2949            assert!(!buffer.is_dirty());
2950            assert!(!buffer.has_conflict());
2951        });
2952        let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
2953        fs::write(&abs_path, new_contents).unwrap();
2954
2955        // Because the buffer was not modified, it is reloaded from disk. Its
2956        // contents are edited according to the diff between the old and new
2957        // file contents.
2958        buffer
2959            .condition(&cx, |buffer, _| buffer.text() == new_contents)
2960            .await;
2961
2962        buffer.update(&mut cx, |buffer, _| {
2963            assert_eq!(buffer.text(), new_contents);
2964            assert!(!buffer.is_dirty());
2965            assert!(!buffer.has_conflict());
2966
2967            // TODO
2968            // let cursor_positions = buffer
2969            //     .selection_set(selection_set_id)
2970            //     .unwrap()
2971            //     .selections::<Point>(&*buffer)
2972            //     .map(|selection| {
2973            //         assert_eq!(selection.start, selection.end);
2974            //         selection.start
2975            //     })
2976            //     .collect::<Vec<_>>();
2977            // assert_eq!(
2978            //     cursor_positions,
2979            //     [Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
2980            // );
2981        });
2982
2983        // Modify the buffer
2984        buffer.update(&mut cx, |buffer, cx| {
2985            buffer.edit(vec![0..0], " ", cx);
2986            assert!(buffer.is_dirty());
2987            assert!(!buffer.has_conflict());
2988        });
2989
2990        // Change the file on disk again, adding blank lines to the beginning.
2991        fs::write(&abs_path, "\n\n\nAAAA\naaa\nBB\nbbbbb\n").unwrap();
2992
2993        // Because the buffer is modified, it doesn't reload from disk, but is
2994        // marked as having a conflict.
2995        buffer
2996            .condition(&cx, |buffer, _| buffer.has_conflict())
2997            .await;
2998    }
2999
3000    #[gpui::test]
3001    async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
3002        let fs = Arc::new(FakeFs::new());
3003        fs.insert_tree(
3004            "/the-dir",
3005            json!({
3006                "a.rs": "
3007                    fn foo(mut v: Vec<usize>) {
3008                        for x in &v {
3009                            v.push(1);
3010                        }
3011                    }
3012                "
3013                .unindent(),
3014            }),
3015        )
3016        .await;
3017
3018        let project = build_project(fs.clone(), &mut cx);
3019        let (worktree, _) = project
3020            .update(&mut cx, |p, cx| {
3021                p.find_or_create_local_worktree("/the-dir", false, cx)
3022            })
3023            .await
3024            .unwrap();
3025        let worktree_id = worktree.read_with(&cx, |tree, _| tree.id());
3026
3027        let buffer = project
3028            .update(&mut cx, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
3029            .await
3030            .unwrap();
3031
3032        let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
3033        let message = lsp::PublishDiagnosticsParams {
3034            uri: buffer_uri.clone(),
3035            diagnostics: vec![
3036                lsp::Diagnostic {
3037                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
3038                    severity: Some(DiagnosticSeverity::WARNING),
3039                    message: "error 1".to_string(),
3040                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3041                        location: lsp::Location {
3042                            uri: buffer_uri.clone(),
3043                            range: lsp::Range::new(
3044                                lsp::Position::new(1, 8),
3045                                lsp::Position::new(1, 9),
3046                            ),
3047                        },
3048                        message: "error 1 hint 1".to_string(),
3049                    }]),
3050                    ..Default::default()
3051                },
3052                lsp::Diagnostic {
3053                    range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)),
3054                    severity: Some(DiagnosticSeverity::HINT),
3055                    message: "error 1 hint 1".to_string(),
3056                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3057                        location: lsp::Location {
3058                            uri: buffer_uri.clone(),
3059                            range: lsp::Range::new(
3060                                lsp::Position::new(1, 8),
3061                                lsp::Position::new(1, 9),
3062                            ),
3063                        },
3064                        message: "original diagnostic".to_string(),
3065                    }]),
3066                    ..Default::default()
3067                },
3068                lsp::Diagnostic {
3069                    range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)),
3070                    severity: Some(DiagnosticSeverity::ERROR),
3071                    message: "error 2".to_string(),
3072                    related_information: Some(vec![
3073                        lsp::DiagnosticRelatedInformation {
3074                            location: lsp::Location {
3075                                uri: buffer_uri.clone(),
3076                                range: lsp::Range::new(
3077                                    lsp::Position::new(1, 13),
3078                                    lsp::Position::new(1, 15),
3079                                ),
3080                            },
3081                            message: "error 2 hint 1".to_string(),
3082                        },
3083                        lsp::DiagnosticRelatedInformation {
3084                            location: lsp::Location {
3085                                uri: buffer_uri.clone(),
3086                                range: lsp::Range::new(
3087                                    lsp::Position::new(1, 13),
3088                                    lsp::Position::new(1, 15),
3089                                ),
3090                            },
3091                            message: "error 2 hint 2".to_string(),
3092                        },
3093                    ]),
3094                    ..Default::default()
3095                },
3096                lsp::Diagnostic {
3097                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
3098                    severity: Some(DiagnosticSeverity::HINT),
3099                    message: "error 2 hint 1".to_string(),
3100                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3101                        location: lsp::Location {
3102                            uri: buffer_uri.clone(),
3103                            range: lsp::Range::new(
3104                                lsp::Position::new(2, 8),
3105                                lsp::Position::new(2, 17),
3106                            ),
3107                        },
3108                        message: "original diagnostic".to_string(),
3109                    }]),
3110                    ..Default::default()
3111                },
3112                lsp::Diagnostic {
3113                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)),
3114                    severity: Some(DiagnosticSeverity::HINT),
3115                    message: "error 2 hint 2".to_string(),
3116                    related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3117                        location: lsp::Location {
3118                            uri: buffer_uri.clone(),
3119                            range: lsp::Range::new(
3120                                lsp::Position::new(2, 8),
3121                                lsp::Position::new(2, 17),
3122                            ),
3123                        },
3124                        message: "original diagnostic".to_string(),
3125                    }]),
3126                    ..Default::default()
3127                },
3128            ],
3129            version: None,
3130        };
3131
3132        project
3133            .update(&mut cx, |p, cx| {
3134                p.update_diagnostics(message, &Default::default(), cx)
3135            })
3136            .unwrap();
3137        let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
3138
3139        assert_eq!(
3140            buffer
3141                .diagnostics_in_range::<_, Point>(0..buffer.len())
3142                .collect::<Vec<_>>(),
3143            &[
3144                DiagnosticEntry {
3145                    range: Point::new(1, 8)..Point::new(1, 9),
3146                    diagnostic: Diagnostic {
3147                        severity: DiagnosticSeverity::WARNING,
3148                        message: "error 1".to_string(),
3149                        group_id: 0,
3150                        is_primary: true,
3151                        ..Default::default()
3152                    }
3153                },
3154                DiagnosticEntry {
3155                    range: Point::new(1, 8)..Point::new(1, 9),
3156                    diagnostic: Diagnostic {
3157                        severity: DiagnosticSeverity::HINT,
3158                        message: "error 1 hint 1".to_string(),
3159                        group_id: 0,
3160                        is_primary: false,
3161                        ..Default::default()
3162                    }
3163                },
3164                DiagnosticEntry {
3165                    range: Point::new(1, 13)..Point::new(1, 15),
3166                    diagnostic: Diagnostic {
3167                        severity: DiagnosticSeverity::HINT,
3168                        message: "error 2 hint 1".to_string(),
3169                        group_id: 1,
3170                        is_primary: false,
3171                        ..Default::default()
3172                    }
3173                },
3174                DiagnosticEntry {
3175                    range: Point::new(1, 13)..Point::new(1, 15),
3176                    diagnostic: Diagnostic {
3177                        severity: DiagnosticSeverity::HINT,
3178                        message: "error 2 hint 2".to_string(),
3179                        group_id: 1,
3180                        is_primary: false,
3181                        ..Default::default()
3182                    }
3183                },
3184                DiagnosticEntry {
3185                    range: Point::new(2, 8)..Point::new(2, 17),
3186                    diagnostic: Diagnostic {
3187                        severity: DiagnosticSeverity::ERROR,
3188                        message: "error 2".to_string(),
3189                        group_id: 1,
3190                        is_primary: true,
3191                        ..Default::default()
3192                    }
3193                }
3194            ]
3195        );
3196
3197        assert_eq!(
3198            buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
3199            &[
3200                DiagnosticEntry {
3201                    range: Point::new(1, 8)..Point::new(1, 9),
3202                    diagnostic: Diagnostic {
3203                        severity: DiagnosticSeverity::WARNING,
3204                        message: "error 1".to_string(),
3205                        group_id: 0,
3206                        is_primary: true,
3207                        ..Default::default()
3208                    }
3209                },
3210                DiagnosticEntry {
3211                    range: Point::new(1, 8)..Point::new(1, 9),
3212                    diagnostic: Diagnostic {
3213                        severity: DiagnosticSeverity::HINT,
3214                        message: "error 1 hint 1".to_string(),
3215                        group_id: 0,
3216                        is_primary: false,
3217                        ..Default::default()
3218                    }
3219                },
3220            ]
3221        );
3222        assert_eq!(
3223            buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
3224            &[
3225                DiagnosticEntry {
3226                    range: Point::new(1, 13)..Point::new(1, 15),
3227                    diagnostic: Diagnostic {
3228                        severity: DiagnosticSeverity::HINT,
3229                        message: "error 2 hint 1".to_string(),
3230                        group_id: 1,
3231                        is_primary: false,
3232                        ..Default::default()
3233                    }
3234                },
3235                DiagnosticEntry {
3236                    range: Point::new(1, 13)..Point::new(1, 15),
3237                    diagnostic: Diagnostic {
3238                        severity: DiagnosticSeverity::HINT,
3239                        message: "error 2 hint 2".to_string(),
3240                        group_id: 1,
3241                        is_primary: false,
3242                        ..Default::default()
3243                    }
3244                },
3245                DiagnosticEntry {
3246                    range: Point::new(2, 8)..Point::new(2, 17),
3247                    diagnostic: Diagnostic {
3248                        severity: DiagnosticSeverity::ERROR,
3249                        message: "error 2".to_string(),
3250                        group_id: 1,
3251                        is_primary: true,
3252                        ..Default::default()
3253                    }
3254                }
3255            ]
3256        );
3257    }
3258
3259    fn build_project(fs: Arc<dyn Fs>, cx: &mut TestAppContext) -> ModelHandle<Project> {
3260        let languages = Arc::new(LanguageRegistry::new());
3261        let http_client = FakeHttpClient::with_404_response();
3262        let client = client::Client::new(http_client.clone());
3263        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
3264        cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
3265    }
3266}