project.rs

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