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