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