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    Bias, Buffer, DiagnosticEntry, File as _, Language, LanguageRegistry, ToOffset, ToPointUtf16,
  17};
  18use lsp::{DiagnosticSeverity, LanguageServer};
  19use postage::{prelude::Stream, watch};
  20use smol::block_on;
  21use std::{
  22    ops::Range,
  23    path::{Path, PathBuf},
  24    sync::{atomic::AtomicBool, Arc},
  25};
  26use util::{ResultExt, TryFutureExt as _};
  27
  28pub use fs::*;
  29pub use worktree::*;
  30
  31pub struct Project {
  32    worktrees: Vec<WorktreeHandle>,
  33    active_entry: Option<ProjectEntry>,
  34    languages: Arc<LanguageRegistry>,
  35    language_servers: HashMap<(WorktreeId, String), Arc<LanguageServer>>,
  36    client: Arc<client::Client>,
  37    user_store: ModelHandle<UserStore>,
  38    fs: Arc<dyn Fs>,
  39    client_state: ProjectClientState,
  40    collaborators: HashMap<PeerId, Collaborator>,
  41    subscriptions: Vec<client::Subscription>,
  42    language_servers_with_diagnostics_running: isize,
  43}
  44
  45enum WorktreeHandle {
  46    Strong(ModelHandle<Worktree>),
  47    Weak(WeakModelHandle<Worktree>),
  48}
  49
  50enum ProjectClientState {
  51    Local {
  52        is_shared: bool,
  53        remote_id_tx: watch::Sender<Option<u64>>,
  54        remote_id_rx: watch::Receiver<Option<u64>>,
  55        _maintain_remote_id_task: Task<Option<()>>,
  56    },
  57    Remote {
  58        sharing_has_stopped: bool,
  59        remote_id: u64,
  60        replica_id: ReplicaId,
  61    },
  62}
  63
  64#[derive(Clone, Debug)]
  65pub struct Collaborator {
  66    pub user: Arc<User>,
  67    pub peer_id: PeerId,
  68    pub replica_id: ReplicaId,
  69}
  70
  71#[derive(Clone, Debug, PartialEq)]
  72pub enum Event {
  73    ActiveEntryChanged(Option<ProjectEntry>),
  74    WorktreeRemoved(WorktreeId),
  75    DiskBasedDiagnosticsStarted,
  76    DiskBasedDiagnosticsUpdated,
  77    DiskBasedDiagnosticsFinished,
  78    DiagnosticsUpdated(ProjectPath),
  79}
  80
  81#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
  82pub struct ProjectPath {
  83    pub worktree_id: WorktreeId,
  84    pub path: Arc<Path>,
  85}
  86
  87#[derive(Clone, Debug, Default, PartialEq)]
  88pub struct DiagnosticSummary {
  89    pub error_count: usize,
  90    pub warning_count: usize,
  91    pub info_count: usize,
  92    pub hint_count: usize,
  93}
  94
  95#[derive(Debug)]
  96pub struct Definition {
  97    pub source_range: Option<Range<language::Anchor>>,
  98    pub target_buffer: ModelHandle<Buffer>,
  99    pub target_range: Range<language::Anchor>,
 100}
 101
 102impl DiagnosticSummary {
 103    fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
 104        let mut this = Self {
 105            error_count: 0,
 106            warning_count: 0,
 107            info_count: 0,
 108            hint_count: 0,
 109        };
 110
 111        for entry in diagnostics {
 112            if entry.diagnostic.is_primary {
 113                match entry.diagnostic.severity {
 114                    DiagnosticSeverity::ERROR => this.error_count += 1,
 115                    DiagnosticSeverity::WARNING => this.warning_count += 1,
 116                    DiagnosticSeverity::INFORMATION => this.info_count += 1,
 117                    DiagnosticSeverity::HINT => this.hint_count += 1,
 118                    _ => {}
 119                }
 120            }
 121        }
 122
 123        this
 124    }
 125
 126    pub fn to_proto(&self, path: Arc<Path>) -> proto::DiagnosticSummary {
 127        proto::DiagnosticSummary {
 128            path: path.to_string_lossy().to_string(),
 129            error_count: self.error_count as u32,
 130            warning_count: self.warning_count as u32,
 131            info_count: self.info_count as u32,
 132            hint_count: self.hint_count as u32,
 133        }
 134    }
 135}
 136
 137#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 138pub struct ProjectEntry {
 139    pub worktree_id: WorktreeId,
 140    pub entry_id: usize,
 141}
 142
 143impl Project {
 144    pub fn local(
 145        client: Arc<Client>,
 146        user_store: ModelHandle<UserStore>,
 147        languages: Arc<LanguageRegistry>,
 148        fs: Arc<dyn Fs>,
 149        cx: &mut MutableAppContext,
 150    ) -> ModelHandle<Self> {
 151        cx.add_model(|cx: &mut ModelContext<Self>| {
 152            let (remote_id_tx, remote_id_rx) = watch::channel();
 153            let _maintain_remote_id_task = cx.spawn_weak({
 154                let rpc = client.clone();
 155                move |this, mut cx| {
 156                    async move {
 157                        let mut status = rpc.status();
 158                        while let Some(status) = status.recv().await {
 159                            if let Some(this) = this.upgrade(&cx) {
 160                                let remote_id = if let client::Status::Connected { .. } = status {
 161                                    let response = rpc.request(proto::RegisterProject {}).await?;
 162                                    Some(response.project_id)
 163                                } else {
 164                                    None
 165                                };
 166
 167                                if let Some(project_id) = remote_id {
 168                                    let mut registrations = Vec::new();
 169                                    this.update(&mut cx, |this, cx| {
 170                                        for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 171                                            registrations.push(worktree.update(
 172                                                cx,
 173                                                |worktree, cx| {
 174                                                    let worktree = worktree.as_local_mut().unwrap();
 175                                                    worktree.register(project_id, cx)
 176                                                },
 177                                            ));
 178                                        }
 179                                    });
 180                                    for registration in registrations {
 181                                        registration.await?;
 182                                    }
 183                                }
 184                                this.update(&mut cx, |this, cx| this.set_remote_id(remote_id, cx));
 185                            }
 186                        }
 187                        Ok(())
 188                    }
 189                    .log_err()
 190                }
 191            });
 192
 193            Self {
 194                worktrees: Default::default(),
 195                collaborators: Default::default(),
 196                client_state: ProjectClientState::Local {
 197                    is_shared: false,
 198                    remote_id_tx,
 199                    remote_id_rx,
 200                    _maintain_remote_id_task,
 201                },
 202                subscriptions: Vec::new(),
 203                active_entry: None,
 204                languages,
 205                client,
 206                user_store,
 207                fs,
 208                language_servers_with_diagnostics_running: 0,
 209                language_servers: Default::default(),
 210            }
 211        })
 212    }
 213
 214    pub async fn remote(
 215        remote_id: u64,
 216        client: Arc<Client>,
 217        user_store: ModelHandle<UserStore>,
 218        languages: Arc<LanguageRegistry>,
 219        fs: Arc<dyn Fs>,
 220        cx: &mut AsyncAppContext,
 221    ) -> Result<ModelHandle<Self>> {
 222        client.authenticate_and_connect(&cx).await?;
 223
 224        let response = client
 225            .request(proto::JoinProject {
 226                project_id: remote_id,
 227            })
 228            .await?;
 229
 230        let replica_id = response.replica_id as ReplicaId;
 231
 232        let mut worktrees = Vec::new();
 233        for worktree in response.worktrees {
 234            worktrees.push(
 235                Worktree::remote(
 236                    remote_id,
 237                    replica_id,
 238                    worktree,
 239                    client.clone(),
 240                    user_store.clone(),
 241                    cx,
 242                )
 243                .await?,
 244            );
 245        }
 246
 247        let user_ids = response
 248            .collaborators
 249            .iter()
 250            .map(|peer| peer.user_id)
 251            .collect();
 252        user_store
 253            .update(cx, |user_store, cx| user_store.load_users(user_ids, cx))
 254            .await?;
 255        let mut collaborators = HashMap::default();
 256        for message in response.collaborators {
 257            let collaborator = Collaborator::from_proto(message, &user_store, cx).await?;
 258            collaborators.insert(collaborator.peer_id, collaborator);
 259        }
 260
 261        Ok(cx.add_model(|cx| {
 262            let mut this = Self {
 263                worktrees: Vec::new(),
 264                active_entry: None,
 265                collaborators,
 266                languages,
 267                user_store,
 268                fs,
 269                subscriptions: vec![
 270                    client.subscribe_to_entity(remote_id, cx, Self::handle_unshare_project),
 271                    client.subscribe_to_entity(remote_id, cx, Self::handle_add_collaborator),
 272                    client.subscribe_to_entity(remote_id, cx, Self::handle_remove_collaborator),
 273                    client.subscribe_to_entity(remote_id, cx, Self::handle_share_worktree),
 274                    client.subscribe_to_entity(remote_id, cx, Self::handle_unregister_worktree),
 275                    client.subscribe_to_entity(remote_id, cx, Self::handle_update_worktree),
 276                    client.subscribe_to_entity(
 277                        remote_id,
 278                        cx,
 279                        Self::handle_update_diagnostic_summary,
 280                    ),
 281                    client.subscribe_to_entity(
 282                        remote_id,
 283                        cx,
 284                        Self::handle_disk_based_diagnostics_updating,
 285                    ),
 286                    client.subscribe_to_entity(
 287                        remote_id,
 288                        cx,
 289                        Self::handle_disk_based_diagnostics_updated,
 290                    ),
 291                    client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer),
 292                    client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
 293                ],
 294                client,
 295                client_state: ProjectClientState::Remote {
 296                    sharing_has_stopped: false,
 297                    remote_id,
 298                    replica_id,
 299                },
 300                language_servers_with_diagnostics_running: 0,
 301                language_servers: Default::default(),
 302            };
 303            for worktree in worktrees {
 304                this.add_worktree(&worktree, cx);
 305            }
 306            this
 307        }))
 308    }
 309
 310    fn set_remote_id(&mut self, remote_id: Option<u64>, cx: &mut ModelContext<Self>) {
 311        if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
 312            *remote_id_tx.borrow_mut() = remote_id;
 313        }
 314
 315        self.subscriptions.clear();
 316        if let Some(remote_id) = remote_id {
 317            let client = &self.client;
 318            self.subscriptions.extend([
 319                client.subscribe_to_entity(remote_id, cx, Self::handle_open_buffer),
 320                client.subscribe_to_entity(remote_id, cx, Self::handle_close_buffer),
 321                client.subscribe_to_entity(remote_id, cx, Self::handle_add_collaborator),
 322                client.subscribe_to_entity(remote_id, cx, Self::handle_remove_collaborator),
 323                client.subscribe_to_entity(remote_id, cx, Self::handle_update_worktree),
 324                client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer),
 325                client.subscribe_to_entity(remote_id, cx, Self::handle_save_buffer),
 326                client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
 327                client.subscribe_to_entity(remote_id, cx, Self::handle_format_buffer),
 328            ]);
 329        }
 330    }
 331
 332    pub fn remote_id(&self) -> Option<u64> {
 333        match &self.client_state {
 334            ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
 335            ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
 336        }
 337    }
 338
 339    pub fn next_remote_id(&self) -> impl Future<Output = u64> {
 340        let mut id = None;
 341        let mut watch = None;
 342        match &self.client_state {
 343            ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
 344            ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
 345        }
 346
 347        async move {
 348            if let Some(id) = id {
 349                return id;
 350            }
 351            let mut watch = watch.unwrap();
 352            loop {
 353                let id = *watch.borrow();
 354                if let Some(id) = id {
 355                    return id;
 356                }
 357                watch.recv().await;
 358            }
 359        }
 360    }
 361
 362    pub fn replica_id(&self) -> ReplicaId {
 363        match &self.client_state {
 364            ProjectClientState::Local { .. } => 0,
 365            ProjectClientState::Remote { replica_id, .. } => *replica_id,
 366        }
 367    }
 368
 369    pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
 370        &self.collaborators
 371    }
 372
 373    pub fn worktrees<'a>(
 374        &'a self,
 375        cx: &'a AppContext,
 376    ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
 377        self.worktrees
 378            .iter()
 379            .filter_map(move |worktree| worktree.upgrade(cx))
 380    }
 381
 382    pub fn worktree_for_id(
 383        &self,
 384        id: WorktreeId,
 385        cx: &AppContext,
 386    ) -> Option<ModelHandle<Worktree>> {
 387        self.worktrees(cx)
 388            .find(|worktree| worktree.read(cx).id() == id)
 389    }
 390
 391    pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
 392        let rpc = self.client.clone();
 393        cx.spawn(|this, mut cx| async move {
 394            let project_id = this.update(&mut cx, |this, _| {
 395                if let ProjectClientState::Local {
 396                    is_shared,
 397                    remote_id_rx,
 398                    ..
 399                } = &mut this.client_state
 400                {
 401                    *is_shared = true;
 402                    remote_id_rx
 403                        .borrow()
 404                        .ok_or_else(|| anyhow!("no project id"))
 405                } else {
 406                    Err(anyhow!("can't share a remote project"))
 407                }
 408            })?;
 409
 410            rpc.request(proto::ShareProject { project_id }).await?;
 411            let mut tasks = Vec::new();
 412            this.update(&mut cx, |this, cx| {
 413                for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 414                    worktree.update(cx, |worktree, cx| {
 415                        let worktree = worktree.as_local_mut().unwrap();
 416                        tasks.push(worktree.share(cx));
 417                    });
 418                }
 419            });
 420            for task in tasks {
 421                task.await?;
 422            }
 423            this.update(&mut cx, |_, cx| cx.notify());
 424            Ok(())
 425        })
 426    }
 427
 428    pub fn unshare(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
 429        let rpc = self.client.clone();
 430        cx.spawn(|this, mut cx| async move {
 431            let project_id = this.update(&mut cx, |this, _| {
 432                if let ProjectClientState::Local {
 433                    is_shared,
 434                    remote_id_rx,
 435                    ..
 436                } = &mut this.client_state
 437                {
 438                    *is_shared = false;
 439                    remote_id_rx
 440                        .borrow()
 441                        .ok_or_else(|| anyhow!("no project id"))
 442                } else {
 443                    Err(anyhow!("can't share a remote project"))
 444                }
 445            })?;
 446
 447            rpc.send(proto::UnshareProject { project_id }).await?;
 448            this.update(&mut cx, |this, cx| {
 449                this.collaborators.clear();
 450                for worktree in this.worktrees(cx).collect::<Vec<_>>() {
 451                    worktree.update(cx, |worktree, _| {
 452                        worktree.as_local_mut().unwrap().unshare();
 453                    });
 454                }
 455                cx.notify()
 456            });
 457            Ok(())
 458        })
 459    }
 460
 461    pub fn is_read_only(&self) -> bool {
 462        match &self.client_state {
 463            ProjectClientState::Local { .. } => false,
 464            ProjectClientState::Remote {
 465                sharing_has_stopped,
 466                ..
 467            } => *sharing_has_stopped,
 468        }
 469    }
 470
 471    pub fn is_local(&self) -> bool {
 472        match &self.client_state {
 473            ProjectClientState::Local { .. } => true,
 474            ProjectClientState::Remote { .. } => false,
 475        }
 476    }
 477
 478    pub fn open_buffer(
 479        &mut self,
 480        path: ProjectPath,
 481        cx: &mut ModelContext<Self>,
 482    ) -> Task<Result<ModelHandle<Buffer>>> {
 483        let worktree = if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
 484            worktree
 485        } else {
 486            return cx.spawn(|_, _| async move { Err(anyhow!("no such worktree")) });
 487        };
 488        let buffer_task = worktree.update(cx, |worktree, cx| worktree.open_buffer(path.path, cx));
 489        cx.spawn(|this, mut cx| async move {
 490            let (buffer, buffer_is_new) = buffer_task.await?;
 491            if buffer_is_new {
 492                this.update(&mut cx, |this, cx| {
 493                    this.assign_language_to_buffer(worktree, buffer.clone(), cx)
 494                });
 495            }
 496            Ok(buffer)
 497        })
 498    }
 499
 500    pub fn save_buffer_as(
 501        &self,
 502        buffer: ModelHandle<Buffer>,
 503        abs_path: PathBuf,
 504        cx: &mut ModelContext<Project>,
 505    ) -> Task<Result<()>> {
 506        let worktree_task = self.find_or_create_worktree_for_abs_path(&abs_path, false, cx);
 507        cx.spawn(|this, mut cx| async move {
 508            let (worktree, path) = worktree_task.await?;
 509            worktree
 510                .update(&mut cx, |worktree, cx| {
 511                    worktree
 512                        .as_local_mut()
 513                        .unwrap()
 514                        .save_buffer_as(buffer.clone(), path, cx)
 515                })
 516                .await?;
 517            this.update(&mut cx, |this, cx| {
 518                this.assign_language_to_buffer(worktree, buffer, cx)
 519            });
 520            Ok(())
 521        })
 522    }
 523
 524    fn assign_language_to_buffer(
 525        &mut self,
 526        worktree: ModelHandle<Worktree>,
 527        buffer: ModelHandle<Buffer>,
 528        cx: &mut ModelContext<Self>,
 529    ) -> Option<()> {
 530        // Set the buffer's language
 531        let full_path = buffer.read(cx).file()?.full_path();
 532        let language = self.languages.select_language(&full_path)?.clone();
 533        buffer.update(cx, |buffer, cx| {
 534            buffer.set_language(Some(language.clone()), cx);
 535        });
 536
 537        // For local worktrees, start a language server if needed.
 538        let worktree = worktree.read(cx);
 539        let worktree_id = worktree.id();
 540        let worktree_abs_path = worktree.as_local()?.abs_path().clone();
 541        let language_server = match self
 542            .language_servers
 543            .entry((worktree_id, language.name().to_string()))
 544        {
 545            hash_map::Entry::Occupied(e) => Some(e.get().clone()),
 546            hash_map::Entry::Vacant(e) => {
 547                Self::start_language_server(self.client.clone(), language, &worktree_abs_path, cx)
 548                    .map(|server| e.insert(server).clone())
 549            }
 550        };
 551
 552        buffer.update(cx, |buffer, cx| {
 553            buffer.set_language_server(language_server, cx)
 554        });
 555
 556        None
 557    }
 558
 559    fn start_language_server(
 560        rpc: Arc<Client>,
 561        language: Arc<Language>,
 562        worktree_path: &Path,
 563        cx: &mut ModelContext<Self>,
 564    ) -> Option<Arc<LanguageServer>> {
 565        enum LspEvent {
 566            DiagnosticsStart,
 567            DiagnosticsUpdate(lsp::PublishDiagnosticsParams),
 568            DiagnosticsFinish,
 569        }
 570
 571        let language_server = language
 572            .start_server(worktree_path, cx)
 573            .log_err()
 574            .flatten()?;
 575        let disk_based_sources = language
 576            .disk_based_diagnostic_sources()
 577            .cloned()
 578            .unwrap_or_default();
 579        let disk_based_diagnostics_progress_token =
 580            language.disk_based_diagnostics_progress_token().cloned();
 581        let has_disk_based_diagnostic_progress_token =
 582            disk_based_diagnostics_progress_token.is_some();
 583        let (diagnostics_tx, diagnostics_rx) = smol::channel::unbounded();
 584
 585        // Listen for `PublishDiagnostics` notifications.
 586        language_server
 587            .on_notification::<lsp::notification::PublishDiagnostics, _>({
 588                let diagnostics_tx = diagnostics_tx.clone();
 589                move |params| {
 590                    if !has_disk_based_diagnostic_progress_token {
 591                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 592                    }
 593                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsUpdate(params))).ok();
 594                    if !has_disk_based_diagnostic_progress_token {
 595                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 596                    }
 597                }
 598            })
 599            .detach();
 600
 601        // Listen for `Progress` notifications. Send an event when the language server
 602        // transitions between running jobs and not running any jobs.
 603        let mut running_jobs_for_this_server: i32 = 0;
 604        language_server
 605            .on_notification::<lsp::notification::Progress, _>(move |params| {
 606                let token = match params.token {
 607                    lsp::NumberOrString::Number(_) => None,
 608                    lsp::NumberOrString::String(token) => Some(token),
 609                };
 610
 611                if token == disk_based_diagnostics_progress_token {
 612                    match params.value {
 613                        lsp::ProgressParamsValue::WorkDone(progress) => match progress {
 614                            lsp::WorkDoneProgress::Begin(_) => {
 615                                running_jobs_for_this_server += 1;
 616                                if running_jobs_for_this_server == 1 {
 617                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 618                                }
 619                            }
 620                            lsp::WorkDoneProgress::End(_) => {
 621                                running_jobs_for_this_server -= 1;
 622                                if running_jobs_for_this_server == 0 {
 623                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 624                                }
 625                            }
 626                            _ => {}
 627                        },
 628                    }
 629                }
 630            })
 631            .detach();
 632
 633        // Process all the LSP events.
 634        cx.spawn_weak(|this, mut cx| async move {
 635            while let Ok(message) = diagnostics_rx.recv().await {
 636                let this = cx.read(|cx| this.upgrade(cx))?;
 637                match message {
 638                    LspEvent::DiagnosticsStart => {
 639                        let send = this.update(&mut cx, |this, cx| {
 640                            this.disk_based_diagnostics_started(cx);
 641                            this.remote_id().map(|project_id| {
 642                                rpc.send(proto::DiskBasedDiagnosticsUpdating { project_id })
 643                            })
 644                        });
 645                        if let Some(send) = send {
 646                            send.await.log_err();
 647                        }
 648                    }
 649                    LspEvent::DiagnosticsUpdate(params) => {
 650                        this.update(&mut cx, |this, cx| {
 651                            this.update_diagnostics(params, &disk_based_sources, cx)
 652                                .log_err();
 653                        });
 654                    }
 655                    LspEvent::DiagnosticsFinish => {
 656                        let send = this.update(&mut cx, |this, cx| {
 657                            this.disk_based_diagnostics_finished(cx);
 658                            this.remote_id().map(|project_id| {
 659                                rpc.send(proto::DiskBasedDiagnosticsUpdated { project_id })
 660                            })
 661                        });
 662                        if let Some(send) = send {
 663                            send.await.log_err();
 664                        }
 665                    }
 666                }
 667            }
 668            Some(())
 669        })
 670        .detach();
 671
 672        Some(language_server)
 673    }
 674
 675    fn update_diagnostics(
 676        &mut self,
 677        diagnostics: lsp::PublishDiagnosticsParams,
 678        disk_based_sources: &HashSet<String>,
 679        cx: &mut ModelContext<Self>,
 680    ) -> Result<()> {
 681        let path = diagnostics
 682            .uri
 683            .to_file_path()
 684            .map_err(|_| anyhow!("URI is not a file"))?;
 685        let (worktree, relative_path) = self
 686            .find_worktree_for_abs_path(&path, cx)
 687            .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
 688        let project_path = ProjectPath {
 689            worktree_id: worktree.read(cx).id(),
 690            path: relative_path.into(),
 691        };
 692        worktree.update(cx, |worktree, cx| {
 693            worktree.as_local_mut().unwrap().update_diagnostics(
 694                project_path.path.clone(),
 695                diagnostics,
 696                disk_based_sources,
 697                cx,
 698            )
 699        })?;
 700        cx.emit(Event::DiagnosticsUpdated(project_path));
 701        Ok(())
 702    }
 703
 704    pub fn definition<T: ToOffset>(
 705        &self,
 706        source_buffer_handle: &ModelHandle<Buffer>,
 707        position: T,
 708        cx: &mut ModelContext<Self>,
 709    ) -> Task<Result<Vec<Definition>>> {
 710        let source_buffer_handle = source_buffer_handle.clone();
 711        let buffer = source_buffer_handle.read(cx);
 712        let worktree;
 713        let buffer_abs_path;
 714        if let Some(file) = File::from_dyn(buffer.file()) {
 715            worktree = file.worktree.clone();
 716            buffer_abs_path = file.abs_path();
 717        } else {
 718            return Task::ready(Err(anyhow!("buffer does not belong to any worktree")));
 719        };
 720
 721        if worktree.read(cx).as_local().is_some() {
 722            let point = buffer.offset_to_point_utf16(position.to_offset(buffer));
 723            let buffer_abs_path = buffer_abs_path.unwrap();
 724            let lang_name;
 725            let lang_server;
 726            if let Some(lang) = buffer.language() {
 727                lang_name = lang.name().to_string();
 728                if let Some(server) = self
 729                    .language_servers
 730                    .get(&(worktree.read(cx).id(), lang_name.clone()))
 731                {
 732                    lang_server = server.clone();
 733                } else {
 734                    return Task::ready(Err(anyhow!("buffer does not have a language server")));
 735                };
 736            } else {
 737                return Task::ready(Err(anyhow!("buffer does not have a language")));
 738            }
 739
 740            cx.spawn(|this, mut cx| async move {
 741                let response = lang_server
 742                    .request::<lsp::request::GotoDefinition>(lsp::GotoDefinitionParams {
 743                        text_document_position_params: lsp::TextDocumentPositionParams {
 744                            text_document: lsp::TextDocumentIdentifier::new(
 745                                lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
 746                            ),
 747                            position: lsp::Position::new(point.row, point.column),
 748                        },
 749                        work_done_progress_params: Default::default(),
 750                        partial_result_params: Default::default(),
 751                    })
 752                    .await?;
 753
 754                let mut definitions = Vec::new();
 755                if let Some(response) = response {
 756                    let mut unresolved_locations = Vec::new();
 757                    match response {
 758                        lsp::GotoDefinitionResponse::Scalar(loc) => {
 759                            unresolved_locations.push((None, loc.uri, loc.range));
 760                        }
 761                        lsp::GotoDefinitionResponse::Array(locs) => {
 762                            unresolved_locations
 763                                .extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
 764                        }
 765                        lsp::GotoDefinitionResponse::Link(links) => {
 766                            unresolved_locations.extend(links.into_iter().map(|l| {
 767                                (
 768                                    l.origin_selection_range,
 769                                    l.target_uri,
 770                                    l.target_selection_range,
 771                                )
 772                            }));
 773                        }
 774                    }
 775
 776                    for (source_range, target_uri, target_range) in unresolved_locations {
 777                        let abs_path = target_uri
 778                            .to_file_path()
 779                            .map_err(|_| anyhow!("invalid target path"))?;
 780
 781                        let (worktree, relative_path) = if let Some(result) = this
 782                            .read_with(&cx, |this, cx| {
 783                                this.find_worktree_for_abs_path(&abs_path, cx)
 784                            }) {
 785                            result
 786                        } else {
 787                            let (worktree, relative_path) = this
 788                                .update(&mut cx, |this, cx| {
 789                                    this.create_worktree_for_abs_path(&abs_path, true, cx)
 790                                })
 791                                .await?;
 792                            this.update(&mut cx, |this, cx| {
 793                                this.language_servers.insert(
 794                                    (worktree.read(cx).id(), lang_name.clone()),
 795                                    lang_server.clone(),
 796                                );
 797                            });
 798                            (worktree, relative_path)
 799                        };
 800
 801                        let project_path = ProjectPath {
 802                            worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
 803                            path: relative_path.into(),
 804                        };
 805                        let target_buffer_handle = this
 806                            .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
 807                            .await?;
 808                        cx.read(|cx| {
 809                            let source_buffer = source_buffer_handle.read(cx);
 810                            let target_buffer = target_buffer_handle.read(cx);
 811                            let source_range = source_range.map(|range| {
 812                                let start = source_buffer
 813                                    .clip_point_utf16(range.start.to_point_utf16(), Bias::Left);
 814                                let end = source_buffer
 815                                    .clip_point_utf16(range.end.to_point_utf16(), Bias::Left);
 816                                source_buffer.anchor_after(start)..source_buffer.anchor_before(end)
 817                            });
 818                            let target_start = target_buffer
 819                                .clip_point_utf16(target_range.start.to_point_utf16(), Bias::Left);
 820                            let target_end = target_buffer
 821                                .clip_point_utf16(target_range.end.to_point_utf16(), Bias::Left);
 822                            definitions.push(Definition {
 823                                source_range,
 824                                target_buffer: target_buffer_handle,
 825                                target_range: target_buffer.anchor_after(target_start)
 826                                    ..target_buffer.anchor_before(target_end),
 827                            });
 828                        });
 829                    }
 830                }
 831
 832                Ok(definitions)
 833            })
 834        } else {
 835            todo!()
 836        }
 837    }
 838
 839    pub fn find_or_create_worktree_for_abs_path(
 840        &self,
 841        abs_path: impl AsRef<Path>,
 842        weak: bool,
 843        cx: &mut ModelContext<Self>,
 844    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 845        let abs_path = abs_path.as_ref();
 846        if let Some((tree, relative_path)) = self.find_worktree_for_abs_path(abs_path, cx) {
 847            Task::ready(Ok((tree.clone(), relative_path.into())))
 848        } else {
 849            self.create_worktree_for_abs_path(abs_path, weak, cx)
 850        }
 851    }
 852
 853    fn create_worktree_for_abs_path(
 854        &self,
 855        abs_path: &Path,
 856        weak: bool,
 857        cx: &mut ModelContext<Self>,
 858    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 859        let worktree = self.add_local_worktree(abs_path, weak, cx);
 860        cx.background().spawn(async move {
 861            let worktree = worktree.await?;
 862            Ok((worktree, PathBuf::new()))
 863        })
 864    }
 865
 866    fn find_worktree_for_abs_path(
 867        &self,
 868        abs_path: &Path,
 869        cx: &AppContext,
 870    ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
 871        for tree in self.worktrees(cx) {
 872            if let Some(relative_path) = tree
 873                .read(cx)
 874                .as_local()
 875                .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
 876            {
 877                return Some((tree.clone(), relative_path.into()));
 878            }
 879        }
 880        None
 881    }
 882
 883    pub fn is_shared(&self) -> bool {
 884        match &self.client_state {
 885            ProjectClientState::Local { is_shared, .. } => *is_shared,
 886            ProjectClientState::Remote { .. } => false,
 887        }
 888    }
 889
 890    fn add_local_worktree(
 891        &self,
 892        abs_path: impl AsRef<Path>,
 893        weak: bool,
 894        cx: &mut ModelContext<Self>,
 895    ) -> Task<Result<ModelHandle<Worktree>>> {
 896        let fs = self.fs.clone();
 897        let client = self.client.clone();
 898        let user_store = self.user_store.clone();
 899        let path = Arc::from(abs_path.as_ref());
 900        cx.spawn(|project, mut cx| async move {
 901            let worktree =
 902                Worktree::open_local(client.clone(), user_store, path, weak, fs, &mut cx).await?;
 903
 904            let (remote_project_id, is_shared) = project.update(&mut cx, |project, cx| {
 905                project.add_worktree(&worktree, cx);
 906                (project.remote_id(), project.is_shared())
 907            });
 908
 909            if let Some(project_id) = remote_project_id {
 910                worktree
 911                    .update(&mut cx, |worktree, cx| {
 912                        worktree.as_local_mut().unwrap().register(project_id, cx)
 913                    })
 914                    .await?;
 915                if is_shared {
 916                    worktree
 917                        .update(&mut cx, |worktree, cx| {
 918                            worktree.as_local_mut().unwrap().share(cx)
 919                        })
 920                        .await?;
 921                }
 922            }
 923
 924            Ok(worktree)
 925        })
 926    }
 927
 928    pub fn remove_worktree(&mut self, id: WorktreeId, cx: &mut ModelContext<Self>) {
 929        self.worktrees.retain(|worktree| {
 930            worktree
 931                .upgrade(cx)
 932                .map_or(false, |w| w.read(cx).id() != id)
 933        });
 934        cx.notify();
 935    }
 936
 937    fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
 938        cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
 939
 940        let push_weak_handle = {
 941            let worktree = worktree.read(cx);
 942            worktree.is_local() && worktree.is_weak()
 943        };
 944        if push_weak_handle {
 945            cx.observe_release(&worktree, |this, cx| {
 946                this.worktrees
 947                    .retain(|worktree| worktree.upgrade(cx).is_some());
 948                cx.notify();
 949            })
 950            .detach();
 951            self.worktrees
 952                .push(WorktreeHandle::Weak(worktree.downgrade()));
 953        } else {
 954            self.worktrees
 955                .push(WorktreeHandle::Strong(worktree.clone()));
 956        }
 957        cx.notify();
 958    }
 959
 960    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
 961        let new_active_entry = entry.and_then(|project_path| {
 962            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
 963            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
 964            Some(ProjectEntry {
 965                worktree_id: project_path.worktree_id,
 966                entry_id: entry.id,
 967            })
 968        });
 969        if new_active_entry != self.active_entry {
 970            self.active_entry = new_active_entry;
 971            cx.emit(Event::ActiveEntryChanged(new_active_entry));
 972        }
 973    }
 974
 975    pub fn path_for_entry(&self, entry: ProjectEntry, cx: &AppContext) -> Option<ProjectPath> {
 976        let worktree = self.worktree_for_id(entry.worktree_id, cx)?.read(cx);
 977        Some(ProjectPath {
 978            worktree_id: entry.worktree_id,
 979            path: worktree.entry_for_id(entry.entry_id)?.path.clone(),
 980        })
 981    }
 982
 983    pub fn is_running_disk_based_diagnostics(&self) -> bool {
 984        self.language_servers_with_diagnostics_running > 0
 985    }
 986
 987    pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
 988        let mut summary = DiagnosticSummary::default();
 989        for (_, path_summary) in self.diagnostic_summaries(cx) {
 990            summary.error_count += path_summary.error_count;
 991            summary.warning_count += path_summary.warning_count;
 992            summary.info_count += path_summary.info_count;
 993            summary.hint_count += path_summary.hint_count;
 994        }
 995        summary
 996    }
 997
 998    pub fn diagnostic_summaries<'a>(
 999        &'a self,
1000        cx: &'a AppContext,
1001    ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
1002        self.worktrees(cx).flat_map(move |worktree| {
1003            let worktree = worktree.read(cx);
1004            let worktree_id = worktree.id();
1005            worktree
1006                .diagnostic_summaries()
1007                .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
1008        })
1009    }
1010
1011    fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
1012        self.language_servers_with_diagnostics_running += 1;
1013        if self.language_servers_with_diagnostics_running == 1 {
1014            cx.emit(Event::DiskBasedDiagnosticsStarted);
1015        }
1016    }
1017
1018    fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
1019        cx.emit(Event::DiskBasedDiagnosticsUpdated);
1020        self.language_servers_with_diagnostics_running -= 1;
1021        if self.language_servers_with_diagnostics_running == 0 {
1022            cx.emit(Event::DiskBasedDiagnosticsFinished);
1023        }
1024    }
1025
1026    pub fn active_entry(&self) -> Option<ProjectEntry> {
1027        self.active_entry
1028    }
1029
1030    // RPC message handlers
1031
1032    fn handle_unshare_project(
1033        &mut self,
1034        _: TypedEnvelope<proto::UnshareProject>,
1035        _: Arc<Client>,
1036        cx: &mut ModelContext<Self>,
1037    ) -> Result<()> {
1038        if let ProjectClientState::Remote {
1039            sharing_has_stopped,
1040            ..
1041        } = &mut self.client_state
1042        {
1043            *sharing_has_stopped = true;
1044            self.collaborators.clear();
1045            cx.notify();
1046            Ok(())
1047        } else {
1048            unreachable!()
1049        }
1050    }
1051
1052    fn handle_add_collaborator(
1053        &mut self,
1054        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
1055        _: Arc<Client>,
1056        cx: &mut ModelContext<Self>,
1057    ) -> Result<()> {
1058        let user_store = self.user_store.clone();
1059        let collaborator = envelope
1060            .payload
1061            .collaborator
1062            .take()
1063            .ok_or_else(|| anyhow!("empty collaborator"))?;
1064
1065        cx.spawn(|this, mut cx| {
1066            async move {
1067                let collaborator =
1068                    Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
1069                this.update(&mut cx, |this, cx| {
1070                    this.collaborators
1071                        .insert(collaborator.peer_id, collaborator);
1072                    cx.notify();
1073                });
1074                Ok(())
1075            }
1076            .log_err()
1077        })
1078        .detach();
1079
1080        Ok(())
1081    }
1082
1083    fn handle_remove_collaborator(
1084        &mut self,
1085        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
1086        _: Arc<Client>,
1087        cx: &mut ModelContext<Self>,
1088    ) -> Result<()> {
1089        let peer_id = PeerId(envelope.payload.peer_id);
1090        let replica_id = self
1091            .collaborators
1092            .remove(&peer_id)
1093            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
1094            .replica_id;
1095        for worktree in self.worktrees(cx).collect::<Vec<_>>() {
1096            worktree.update(cx, |worktree, cx| {
1097                worktree.remove_collaborator(peer_id, replica_id, cx);
1098            })
1099        }
1100        Ok(())
1101    }
1102
1103    fn handle_share_worktree(
1104        &mut self,
1105        envelope: TypedEnvelope<proto::ShareWorktree>,
1106        client: Arc<Client>,
1107        cx: &mut ModelContext<Self>,
1108    ) -> Result<()> {
1109        let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
1110        let replica_id = self.replica_id();
1111        let worktree = envelope
1112            .payload
1113            .worktree
1114            .ok_or_else(|| anyhow!("invalid worktree"))?;
1115        let user_store = self.user_store.clone();
1116        cx.spawn(|this, mut cx| {
1117            async move {
1118                let worktree =
1119                    Worktree::remote(remote_id, replica_id, worktree, client, user_store, &mut cx)
1120                        .await?;
1121                this.update(&mut cx, |this, cx| this.add_worktree(&worktree, cx));
1122                Ok(())
1123            }
1124            .log_err()
1125        })
1126        .detach();
1127        Ok(())
1128    }
1129
1130    fn handle_unregister_worktree(
1131        &mut self,
1132        envelope: TypedEnvelope<proto::UnregisterWorktree>,
1133        _: Arc<Client>,
1134        cx: &mut ModelContext<Self>,
1135    ) -> Result<()> {
1136        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1137        self.remove_worktree(worktree_id, cx);
1138        Ok(())
1139    }
1140
1141    fn handle_update_worktree(
1142        &mut self,
1143        envelope: TypedEnvelope<proto::UpdateWorktree>,
1144        _: Arc<Client>,
1145        cx: &mut ModelContext<Self>,
1146    ) -> Result<()> {
1147        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1148        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1149            worktree.update(cx, |worktree, cx| {
1150                let worktree = worktree.as_remote_mut().unwrap();
1151                worktree.update_from_remote(envelope, cx)
1152            })?;
1153        }
1154        Ok(())
1155    }
1156
1157    fn handle_update_diagnostic_summary(
1158        &mut self,
1159        envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
1160        _: Arc<Client>,
1161        cx: &mut ModelContext<Self>,
1162    ) -> Result<()> {
1163        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1164        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1165            if let Some(summary) = envelope.payload.summary {
1166                let project_path = ProjectPath {
1167                    worktree_id,
1168                    path: Path::new(&summary.path).into(),
1169                };
1170                worktree.update(cx, |worktree, _| {
1171                    worktree
1172                        .as_remote_mut()
1173                        .unwrap()
1174                        .update_diagnostic_summary(project_path.path.clone(), &summary);
1175                });
1176                cx.emit(Event::DiagnosticsUpdated(project_path));
1177            }
1178        }
1179        Ok(())
1180    }
1181
1182    fn handle_disk_based_diagnostics_updating(
1183        &mut self,
1184        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
1185        _: Arc<Client>,
1186        cx: &mut ModelContext<Self>,
1187    ) -> Result<()> {
1188        self.disk_based_diagnostics_started(cx);
1189        Ok(())
1190    }
1191
1192    fn handle_disk_based_diagnostics_updated(
1193        &mut self,
1194        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,
1195        _: Arc<Client>,
1196        cx: &mut ModelContext<Self>,
1197    ) -> Result<()> {
1198        self.disk_based_diagnostics_finished(cx);
1199        Ok(())
1200    }
1201
1202    pub fn handle_update_buffer(
1203        &mut self,
1204        envelope: TypedEnvelope<proto::UpdateBuffer>,
1205        _: Arc<Client>,
1206        cx: &mut ModelContext<Self>,
1207    ) -> Result<()> {
1208        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1209        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1210            worktree.update(cx, |worktree, cx| {
1211                worktree.handle_update_buffer(envelope, cx)
1212            })?;
1213        }
1214        Ok(())
1215    }
1216
1217    pub fn handle_save_buffer(
1218        &mut self,
1219        envelope: TypedEnvelope<proto::SaveBuffer>,
1220        rpc: Arc<Client>,
1221        cx: &mut ModelContext<Self>,
1222    ) -> Result<()> {
1223        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1224        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1225            worktree.update(cx, |worktree, cx| {
1226                worktree.handle_save_buffer(envelope, rpc, cx)
1227            })?;
1228        }
1229        Ok(())
1230    }
1231
1232    pub fn handle_format_buffer(
1233        &mut self,
1234        envelope: TypedEnvelope<proto::FormatBuffer>,
1235        rpc: Arc<Client>,
1236        cx: &mut ModelContext<Self>,
1237    ) -> Result<()> {
1238        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1239        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1240            worktree.update(cx, |worktree, cx| {
1241                worktree.handle_format_buffer(envelope, rpc, cx)
1242            })?;
1243        }
1244        Ok(())
1245    }
1246
1247    pub fn handle_open_buffer(
1248        &mut self,
1249        envelope: TypedEnvelope<proto::OpenBuffer>,
1250        rpc: Arc<Client>,
1251        cx: &mut ModelContext<Self>,
1252    ) -> anyhow::Result<()> {
1253        let receipt = envelope.receipt();
1254        let peer_id = envelope.original_sender_id()?;
1255        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1256        let worktree = self
1257            .worktree_for_id(worktree_id, cx)
1258            .ok_or_else(|| anyhow!("no such worktree"))?;
1259
1260        let task = self.open_buffer(
1261            ProjectPath {
1262                worktree_id,
1263                path: PathBuf::from(envelope.payload.path).into(),
1264            },
1265            cx,
1266        );
1267        cx.spawn(|_, mut cx| {
1268            async move {
1269                let buffer = task.await?;
1270                let response = worktree.update(&mut cx, |worktree, cx| {
1271                    worktree
1272                        .as_local_mut()
1273                        .unwrap()
1274                        .open_remote_buffer(peer_id, buffer, cx)
1275                });
1276                rpc.respond(receipt, response).await?;
1277                Ok(())
1278            }
1279            .log_err()
1280        })
1281        .detach();
1282        Ok(())
1283    }
1284
1285    pub fn handle_close_buffer(
1286        &mut self,
1287        envelope: TypedEnvelope<proto::CloseBuffer>,
1288        _: Arc<Client>,
1289        cx: &mut ModelContext<Self>,
1290    ) -> anyhow::Result<()> {
1291        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1292        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1293            worktree.update(cx, |worktree, cx| {
1294                worktree
1295                    .as_local_mut()
1296                    .unwrap()
1297                    .close_remote_buffer(envelope, cx)
1298            })?;
1299        }
1300        Ok(())
1301    }
1302
1303    pub fn handle_buffer_saved(
1304        &mut self,
1305        envelope: TypedEnvelope<proto::BufferSaved>,
1306        _: Arc<Client>,
1307        cx: &mut ModelContext<Self>,
1308    ) -> Result<()> {
1309        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1310        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1311            worktree.update(cx, |worktree, cx| {
1312                worktree.handle_buffer_saved(envelope, cx)
1313            })?;
1314        }
1315        Ok(())
1316    }
1317
1318    pub fn match_paths<'a>(
1319        &self,
1320        query: &'a str,
1321        include_ignored: bool,
1322        smart_case: bool,
1323        max_results: usize,
1324        cancel_flag: &'a AtomicBool,
1325        cx: &AppContext,
1326    ) -> impl 'a + Future<Output = Vec<PathMatch>> {
1327        let include_root_name = self.worktrees.len() > 1;
1328        let candidate_sets = self
1329            .worktrees(cx)
1330            .map(|worktree| CandidateSet {
1331                snapshot: worktree.read(cx).snapshot(),
1332                include_ignored,
1333                include_root_name,
1334            })
1335            .collect::<Vec<_>>();
1336
1337        let background = cx.background().clone();
1338        async move {
1339            fuzzy::match_paths(
1340                candidate_sets.as_slice(),
1341                query,
1342                smart_case,
1343                max_results,
1344                cancel_flag,
1345                background,
1346            )
1347            .await
1348        }
1349    }
1350}
1351
1352impl WorktreeHandle {
1353    pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
1354        match self {
1355            WorktreeHandle::Strong(handle) => Some(handle.clone()),
1356            WorktreeHandle::Weak(handle) => handle.upgrade(cx),
1357        }
1358    }
1359}
1360
1361struct CandidateSet {
1362    snapshot: Snapshot,
1363    include_ignored: bool,
1364    include_root_name: bool,
1365}
1366
1367impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
1368    type Candidates = CandidateSetIter<'a>;
1369
1370    fn id(&self) -> usize {
1371        self.snapshot.id().to_usize()
1372    }
1373
1374    fn len(&self) -> usize {
1375        if self.include_ignored {
1376            self.snapshot.file_count()
1377        } else {
1378            self.snapshot.visible_file_count()
1379        }
1380    }
1381
1382    fn prefix(&self) -> Arc<str> {
1383        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
1384            self.snapshot.root_name().into()
1385        } else if self.include_root_name {
1386            format!("{}/", self.snapshot.root_name()).into()
1387        } else {
1388            "".into()
1389        }
1390    }
1391
1392    fn candidates(&'a self, start: usize) -> Self::Candidates {
1393        CandidateSetIter {
1394            traversal: self.snapshot.files(self.include_ignored, start),
1395        }
1396    }
1397}
1398
1399struct CandidateSetIter<'a> {
1400    traversal: Traversal<'a>,
1401}
1402
1403impl<'a> Iterator for CandidateSetIter<'a> {
1404    type Item = PathMatchCandidate<'a>;
1405
1406    fn next(&mut self) -> Option<Self::Item> {
1407        self.traversal.next().map(|entry| {
1408            if let EntryKind::File(char_bag) = entry.kind {
1409                PathMatchCandidate {
1410                    path: &entry.path,
1411                    char_bag,
1412                }
1413            } else {
1414                unreachable!()
1415            }
1416        })
1417    }
1418}
1419
1420impl Entity for Project {
1421    type Event = Event;
1422
1423    fn release(&mut self, cx: &mut gpui::MutableAppContext) {
1424        match &self.client_state {
1425            ProjectClientState::Local { remote_id_rx, .. } => {
1426                if let Some(project_id) = *remote_id_rx.borrow() {
1427                    let rpc = self.client.clone();
1428                    cx.spawn(|_| async move {
1429                        if let Err(err) = rpc.send(proto::UnregisterProject { project_id }).await {
1430                            log::error!("error unregistering project: {}", err);
1431                        }
1432                    })
1433                    .detach();
1434                }
1435            }
1436            ProjectClientState::Remote { remote_id, .. } => {
1437                let rpc = self.client.clone();
1438                let project_id = *remote_id;
1439                cx.spawn(|_| async move {
1440                    if let Err(err) = rpc.send(proto::LeaveProject { project_id }).await {
1441                        log::error!("error leaving project: {}", err);
1442                    }
1443                })
1444                .detach();
1445            }
1446        }
1447    }
1448
1449    fn app_will_quit(
1450        &mut self,
1451        _: &mut MutableAppContext,
1452    ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
1453        use futures::FutureExt;
1454
1455        let shutdown_futures = self
1456            .language_servers
1457            .drain()
1458            .filter_map(|(_, server)| server.shutdown())
1459            .collect::<Vec<_>>();
1460        Some(
1461            async move {
1462                futures::future::join_all(shutdown_futures).await;
1463            }
1464            .boxed(),
1465        )
1466    }
1467}
1468
1469impl Collaborator {
1470    fn from_proto(
1471        message: proto::Collaborator,
1472        user_store: &ModelHandle<UserStore>,
1473        cx: &mut AsyncAppContext,
1474    ) -> impl Future<Output = Result<Self>> {
1475        let user = user_store.update(cx, |user_store, cx| {
1476            user_store.fetch_user(message.user_id, cx)
1477        });
1478
1479        async move {
1480            Ok(Self {
1481                peer_id: PeerId(message.peer_id),
1482                user: user.await?,
1483                replica_id: message.replica_id as ReplicaId,
1484            })
1485        }
1486    }
1487}
1488
1489#[cfg(test)]
1490mod tests {
1491    use super::{Event, *};
1492    use client::test::FakeHttpClient;
1493    use fs::RealFs;
1494    use futures::StreamExt;
1495    use gpui::{test::subscribe, TestAppContext};
1496    use language::{
1497        tree_sitter_rust, Diagnostic, LanguageConfig, LanguageRegistry, LanguageServerConfig, Point,
1498    };
1499    use lsp::Url;
1500    use serde_json::json;
1501    use std::{os::unix, path::PathBuf};
1502    use util::test::temp_tree;
1503
1504    #[gpui::test]
1505    async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
1506        let dir = temp_tree(json!({
1507            "root": {
1508                "apple": "",
1509                "banana": {
1510                    "carrot": {
1511                        "date": "",
1512                        "endive": "",
1513                    }
1514                },
1515                "fennel": {
1516                    "grape": "",
1517                }
1518            }
1519        }));
1520
1521        let root_link_path = dir.path().join("root_link");
1522        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
1523        unix::fs::symlink(
1524            &dir.path().join("root/fennel"),
1525            &dir.path().join("root/finnochio"),
1526        )
1527        .unwrap();
1528
1529        let project = build_project(&mut cx);
1530
1531        let tree = project
1532            .update(&mut cx, |project, cx| {
1533                project.add_local_worktree(&root_link_path, false, cx)
1534            })
1535            .await
1536            .unwrap();
1537
1538        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1539            .await;
1540        cx.read(|cx| {
1541            let tree = tree.read(cx);
1542            assert_eq!(tree.file_count(), 5);
1543            assert_eq!(
1544                tree.inode_for_path("fennel/grape"),
1545                tree.inode_for_path("finnochio/grape")
1546            );
1547        });
1548
1549        let cancel_flag = Default::default();
1550        let results = project
1551            .read_with(&cx, |project, cx| {
1552                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
1553            })
1554            .await;
1555        assert_eq!(
1556            results
1557                .into_iter()
1558                .map(|result| result.path)
1559                .collect::<Vec<Arc<Path>>>(),
1560            vec![
1561                PathBuf::from("banana/carrot/date").into(),
1562                PathBuf::from("banana/carrot/endive").into(),
1563            ]
1564        );
1565    }
1566
1567    #[gpui::test]
1568    async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
1569        let (language_server_config, mut fake_server) =
1570            LanguageServerConfig::fake(cx.background()).await;
1571        let progress_token = language_server_config
1572            .disk_based_diagnostics_progress_token
1573            .clone()
1574            .unwrap();
1575
1576        let mut languages = LanguageRegistry::new();
1577        languages.add(Arc::new(Language::new(
1578            LanguageConfig {
1579                name: "Rust".to_string(),
1580                path_suffixes: vec!["rs".to_string()],
1581                language_server: Some(language_server_config),
1582                ..Default::default()
1583            },
1584            Some(tree_sitter_rust::language()),
1585        )));
1586
1587        let dir = temp_tree(json!({
1588            "a.rs": "fn a() { A }",
1589            "b.rs": "const y: i32 = 1",
1590        }));
1591
1592        let http_client = FakeHttpClient::with_404_response();
1593        let client = Client::new(http_client.clone());
1594        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1595
1596        let project = cx.update(|cx| {
1597            Project::local(
1598                client,
1599                user_store,
1600                Arc::new(languages),
1601                Arc::new(RealFs),
1602                cx,
1603            )
1604        });
1605
1606        let tree = project
1607            .update(&mut cx, |project, cx| {
1608                project.add_local_worktree(dir.path(), false, cx)
1609            })
1610            .await
1611            .unwrap();
1612        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
1613
1614        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1615            .await;
1616
1617        // Cause worktree to start the fake language server
1618        let _buffer = project
1619            .update(&mut cx, |project, cx| {
1620                project.open_buffer(
1621                    ProjectPath {
1622                        worktree_id,
1623                        path: Path::new("b.rs").into(),
1624                    },
1625                    cx,
1626                )
1627            })
1628            .await
1629            .unwrap();
1630
1631        let mut events = subscribe(&project, &mut cx);
1632
1633        fake_server.start_progress(&progress_token).await;
1634        assert_eq!(
1635            events.next().await.unwrap(),
1636            Event::DiskBasedDiagnosticsStarted
1637        );
1638
1639        fake_server.start_progress(&progress_token).await;
1640        fake_server.end_progress(&progress_token).await;
1641        fake_server.start_progress(&progress_token).await;
1642
1643        fake_server
1644            .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
1645                uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
1646                version: None,
1647                diagnostics: vec![lsp::Diagnostic {
1648                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
1649                    severity: Some(lsp::DiagnosticSeverity::ERROR),
1650                    message: "undefined variable 'A'".to_string(),
1651                    ..Default::default()
1652                }],
1653            })
1654            .await;
1655        assert_eq!(
1656            events.next().await.unwrap(),
1657            Event::DiagnosticsUpdated(ProjectPath {
1658                worktree_id,
1659                path: Arc::from(Path::new("a.rs"))
1660            })
1661        );
1662
1663        fake_server.end_progress(&progress_token).await;
1664        fake_server.end_progress(&progress_token).await;
1665        assert_eq!(
1666            events.next().await.unwrap(),
1667            Event::DiskBasedDiagnosticsUpdated
1668        );
1669        assert_eq!(
1670            events.next().await.unwrap(),
1671            Event::DiskBasedDiagnosticsFinished
1672        );
1673
1674        let (buffer, _) = tree
1675            .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))
1676            .await
1677            .unwrap();
1678
1679        buffer.read_with(&cx, |buffer, _| {
1680            let snapshot = buffer.snapshot();
1681            let diagnostics = snapshot
1682                .diagnostics_in_range::<_, Point>(0..buffer.len())
1683                .collect::<Vec<_>>();
1684            assert_eq!(
1685                diagnostics,
1686                &[DiagnosticEntry {
1687                    range: Point::new(0, 9)..Point::new(0, 10),
1688                    diagnostic: Diagnostic {
1689                        severity: lsp::DiagnosticSeverity::ERROR,
1690                        message: "undefined variable 'A'".to_string(),
1691                        group_id: 0,
1692                        is_primary: true,
1693                        ..Default::default()
1694                    }
1695                }]
1696            )
1697        });
1698    }
1699
1700    #[gpui::test]
1701    async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
1702        let dir = temp_tree(json!({
1703            "root": {
1704                "dir1": {},
1705                "dir2": {
1706                    "dir3": {}
1707                }
1708            }
1709        }));
1710
1711        let project = build_project(&mut cx);
1712        let tree = project
1713            .update(&mut cx, |project, cx| {
1714                project.add_local_worktree(&dir.path(), false, cx)
1715            })
1716            .await
1717            .unwrap();
1718
1719        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1720            .await;
1721
1722        let cancel_flag = Default::default();
1723        let results = project
1724            .read_with(&cx, |project, cx| {
1725                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
1726            })
1727            .await;
1728
1729        assert!(results.is_empty());
1730    }
1731
1732    fn build_project(cx: &mut TestAppContext) -> ModelHandle<Project> {
1733        let languages = Arc::new(LanguageRegistry::new());
1734        let fs = Arc::new(RealFs);
1735        let http_client = FakeHttpClient::with_404_response();
1736        let client = client::Client::new(http_client.clone());
1737        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1738        cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
1739    }
1740}