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