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        for tree in &self.worktrees {
 680            let relative_path = tree.update(cx, |tree, _| {
 681                path.strip_prefix(tree.as_local()?.abs_path()).ok()
 682            });
 683            if let Some(relative_path) = relative_path {
 684                let worktree_id = tree.read(cx).id();
 685                let project_path = ProjectPath {
 686                    worktree_id,
 687                    path: relative_path.into(),
 688                };
 689                tree.update(cx, |tree, cx| {
 690                    tree.as_local_mut().unwrap().update_diagnostics(
 691                        project_path.path.clone(),
 692                        diagnostics,
 693                        disk_based_sources,
 694                        cx,
 695                    )
 696                })?;
 697                cx.emit(Event::DiagnosticsUpdated(project_path));
 698                break;
 699            }
 700        }
 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, 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: &Path,
 842        cx: &mut ModelContext<Self>,
 843    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 844        if let Some((tree, relative_path)) = self.find_worktree_for_abs_path(abs_path, cx) {
 845            Task::ready(Ok((tree.clone(), relative_path.into())))
 846        } else {
 847            self.create_worktree_for_abs_path(abs_path, cx)
 848        }
 849    }
 850
 851    fn create_worktree_for_abs_path(
 852        &self,
 853        abs_path: &Path,
 854        cx: &mut ModelContext<Self>,
 855    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 856        let worktree = self.add_local_worktree(abs_path, cx);
 857        cx.background().spawn(async move {
 858            let worktree = worktree.await?;
 859            Ok((worktree, PathBuf::new()))
 860        })
 861    }
 862
 863    fn find_worktree_for_abs_path(
 864        &self,
 865        abs_path: &Path,
 866        cx: &AppContext,
 867    ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
 868        for tree in &self.worktrees {
 869            if let Some(relative_path) = tree
 870                .read(cx)
 871                .as_local()
 872                .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
 873            {
 874                return Some((tree.clone(), relative_path.into()));
 875            }
 876        }
 877        None
 878    }
 879
 880    pub fn is_shared(&self) -> bool {
 881        match &self.client_state {
 882            ProjectClientState::Local { is_shared, .. } => *is_shared,
 883            ProjectClientState::Remote { .. } => false,
 884        }
 885    }
 886
 887    pub fn add_local_worktree(
 888        &self,
 889        abs_path: impl AsRef<Path>,
 890        cx: &mut ModelContext<Self>,
 891    ) -> Task<Result<ModelHandle<Worktree>>> {
 892        let fs = self.fs.clone();
 893        let client = self.client.clone();
 894        let user_store = self.user_store.clone();
 895        let path = Arc::from(abs_path.as_ref());
 896        cx.spawn(|project, mut cx| async move {
 897            let worktree =
 898                Worktree::open_local(client.clone(), user_store, path, fs, &mut cx).await?;
 899
 900            let (remote_project_id, is_shared) = project.update(&mut cx, |project, cx| {
 901                project.add_worktree(worktree.clone(), cx);
 902                (project.remote_id(), project.is_shared())
 903            });
 904
 905            if let Some(project_id) = remote_project_id {
 906                let worktree_id = worktree.id() as u64;
 907                let register_message = worktree.update(&mut cx, |worktree, _| {
 908                    let worktree = worktree.as_local_mut().unwrap();
 909                    proto::RegisterWorktree {
 910                        project_id,
 911                        worktree_id,
 912                        root_name: worktree.root_name().to_string(),
 913                        authorized_logins: worktree.authorized_logins(),
 914                    }
 915                });
 916                client.request(register_message).await?;
 917                if is_shared {
 918                    worktree
 919                        .update(&mut cx, |worktree, cx| {
 920                            worktree.as_local_mut().unwrap().share(project_id, cx)
 921                        })
 922                        .await?;
 923                }
 924            }
 925
 926            Ok(worktree)
 927        })
 928    }
 929
 930    fn add_worktree(&mut self, worktree: ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
 931        cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
 932        self.worktrees.push(worktree);
 933        cx.notify();
 934    }
 935
 936    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
 937        let new_active_entry = entry.and_then(|project_path| {
 938            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
 939            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
 940            Some(ProjectEntry {
 941                worktree_id: project_path.worktree_id,
 942                entry_id: entry.id,
 943            })
 944        });
 945        if new_active_entry != self.active_entry {
 946            self.active_entry = new_active_entry;
 947            cx.emit(Event::ActiveEntryChanged(new_active_entry));
 948        }
 949    }
 950
 951    pub fn path_for_entry(&self, entry: ProjectEntry, cx: &AppContext) -> Option<ProjectPath> {
 952        let worktree = self.worktree_for_id(entry.worktree_id, cx)?.read(cx);
 953        Some(ProjectPath {
 954            worktree_id: entry.worktree_id,
 955            path: worktree.entry_for_id(entry.entry_id)?.path.clone(),
 956        })
 957    }
 958
 959    pub fn is_running_disk_based_diagnostics(&self) -> bool {
 960        self.language_servers_with_diagnostics_running > 0
 961    }
 962
 963    pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
 964        let mut summary = DiagnosticSummary::default();
 965        for (_, path_summary) in self.diagnostic_summaries(cx) {
 966            summary.error_count += path_summary.error_count;
 967            summary.warning_count += path_summary.warning_count;
 968            summary.info_count += path_summary.info_count;
 969            summary.hint_count += path_summary.hint_count;
 970        }
 971        summary
 972    }
 973
 974    pub fn diagnostic_summaries<'a>(
 975        &'a self,
 976        cx: &'a AppContext,
 977    ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
 978        self.worktrees.iter().flat_map(move |worktree| {
 979            let worktree = worktree.read(cx);
 980            let worktree_id = worktree.id();
 981            worktree
 982                .diagnostic_summaries()
 983                .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
 984        })
 985    }
 986
 987    fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
 988        self.language_servers_with_diagnostics_running += 1;
 989        if self.language_servers_with_diagnostics_running == 1 {
 990            cx.emit(Event::DiskBasedDiagnosticsStarted);
 991        }
 992    }
 993
 994    fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
 995        cx.emit(Event::DiskBasedDiagnosticsUpdated);
 996        self.language_servers_with_diagnostics_running -= 1;
 997        if self.language_servers_with_diagnostics_running == 0 {
 998            cx.emit(Event::DiskBasedDiagnosticsFinished);
 999        }
1000    }
1001
1002    pub fn active_entry(&self) -> Option<ProjectEntry> {
1003        self.active_entry
1004    }
1005
1006    // RPC message handlers
1007
1008    fn handle_unshare_project(
1009        &mut self,
1010        _: TypedEnvelope<proto::UnshareProject>,
1011        _: Arc<Client>,
1012        cx: &mut ModelContext<Self>,
1013    ) -> Result<()> {
1014        if let ProjectClientState::Remote {
1015            sharing_has_stopped,
1016            ..
1017        } = &mut self.client_state
1018        {
1019            *sharing_has_stopped = true;
1020            self.collaborators.clear();
1021            cx.notify();
1022            Ok(())
1023        } else {
1024            unreachable!()
1025        }
1026    }
1027
1028    fn handle_add_collaborator(
1029        &mut self,
1030        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
1031        _: Arc<Client>,
1032        cx: &mut ModelContext<Self>,
1033    ) -> Result<()> {
1034        let user_store = self.user_store.clone();
1035        let collaborator = envelope
1036            .payload
1037            .collaborator
1038            .take()
1039            .ok_or_else(|| anyhow!("empty collaborator"))?;
1040
1041        cx.spawn(|this, mut cx| {
1042            async move {
1043                let collaborator =
1044                    Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
1045                this.update(&mut cx, |this, cx| {
1046                    this.collaborators
1047                        .insert(collaborator.peer_id, collaborator);
1048                    cx.notify();
1049                });
1050                Ok(())
1051            }
1052            .log_err()
1053        })
1054        .detach();
1055
1056        Ok(())
1057    }
1058
1059    fn handle_remove_collaborator(
1060        &mut self,
1061        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
1062        _: Arc<Client>,
1063        cx: &mut ModelContext<Self>,
1064    ) -> Result<()> {
1065        let peer_id = PeerId(envelope.payload.peer_id);
1066        let replica_id = self
1067            .collaborators
1068            .remove(&peer_id)
1069            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
1070            .replica_id;
1071        for worktree in &self.worktrees {
1072            worktree.update(cx, |worktree, cx| {
1073                worktree.remove_collaborator(peer_id, replica_id, cx);
1074            })
1075        }
1076        Ok(())
1077    }
1078
1079    fn handle_share_worktree(
1080        &mut self,
1081        envelope: TypedEnvelope<proto::ShareWorktree>,
1082        client: Arc<Client>,
1083        cx: &mut ModelContext<Self>,
1084    ) -> Result<()> {
1085        let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
1086        let replica_id = self.replica_id();
1087        let worktree = envelope
1088            .payload
1089            .worktree
1090            .ok_or_else(|| anyhow!("invalid worktree"))?;
1091        let user_store = self.user_store.clone();
1092        cx.spawn(|this, mut cx| {
1093            async move {
1094                let worktree =
1095                    Worktree::remote(remote_id, replica_id, worktree, client, user_store, &mut cx)
1096                        .await?;
1097                this.update(&mut cx, |this, cx| this.add_worktree(worktree, cx));
1098                Ok(())
1099            }
1100            .log_err()
1101        })
1102        .detach();
1103        Ok(())
1104    }
1105
1106    fn handle_unregister_worktree(
1107        &mut self,
1108        envelope: TypedEnvelope<proto::UnregisterWorktree>,
1109        _: Arc<Client>,
1110        cx: &mut ModelContext<Self>,
1111    ) -> Result<()> {
1112        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1113        self.worktrees
1114            .retain(|worktree| worktree.read(cx).as_remote().unwrap().id() != worktree_id);
1115        cx.notify();
1116        Ok(())
1117    }
1118
1119    fn handle_update_worktree(
1120        &mut self,
1121        envelope: TypedEnvelope<proto::UpdateWorktree>,
1122        _: Arc<Client>,
1123        cx: &mut ModelContext<Self>,
1124    ) -> Result<()> {
1125        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1126        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1127            worktree.update(cx, |worktree, cx| {
1128                let worktree = worktree.as_remote_mut().unwrap();
1129                worktree.update_from_remote(envelope, cx)
1130            })?;
1131        }
1132        Ok(())
1133    }
1134
1135    fn handle_update_diagnostic_summary(
1136        &mut self,
1137        envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
1138        _: Arc<Client>,
1139        cx: &mut ModelContext<Self>,
1140    ) -> Result<()> {
1141        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1142        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1143            if let Some(summary) = envelope.payload.summary {
1144                let project_path = ProjectPath {
1145                    worktree_id,
1146                    path: Path::new(&summary.path).into(),
1147                };
1148                worktree.update(cx, |worktree, _| {
1149                    worktree
1150                        .as_remote_mut()
1151                        .unwrap()
1152                        .update_diagnostic_summary(project_path.path.clone(), &summary);
1153                });
1154                cx.emit(Event::DiagnosticsUpdated(project_path));
1155            }
1156        }
1157        Ok(())
1158    }
1159
1160    fn handle_disk_based_diagnostics_updating(
1161        &mut self,
1162        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
1163        _: Arc<Client>,
1164        cx: &mut ModelContext<Self>,
1165    ) -> Result<()> {
1166        self.disk_based_diagnostics_started(cx);
1167        Ok(())
1168    }
1169
1170    fn handle_disk_based_diagnostics_updated(
1171        &mut self,
1172        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,
1173        _: Arc<Client>,
1174        cx: &mut ModelContext<Self>,
1175    ) -> Result<()> {
1176        self.disk_based_diagnostics_finished(cx);
1177        Ok(())
1178    }
1179
1180    pub fn handle_update_buffer(
1181        &mut self,
1182        envelope: TypedEnvelope<proto::UpdateBuffer>,
1183        _: Arc<Client>,
1184        cx: &mut ModelContext<Self>,
1185    ) -> Result<()> {
1186        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1187        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1188            worktree.update(cx, |worktree, cx| {
1189                worktree.handle_update_buffer(envelope, cx)
1190            })?;
1191        }
1192        Ok(())
1193    }
1194
1195    pub fn handle_save_buffer(
1196        &mut self,
1197        envelope: TypedEnvelope<proto::SaveBuffer>,
1198        rpc: Arc<Client>,
1199        cx: &mut ModelContext<Self>,
1200    ) -> Result<()> {
1201        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1202        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1203            worktree.update(cx, |worktree, cx| {
1204                worktree.handle_save_buffer(envelope, rpc, cx)
1205            })?;
1206        }
1207        Ok(())
1208    }
1209
1210    pub fn handle_format_buffer(
1211        &mut self,
1212        envelope: TypedEnvelope<proto::FormatBuffer>,
1213        rpc: Arc<Client>,
1214        cx: &mut ModelContext<Self>,
1215    ) -> Result<()> {
1216        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1217        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1218            worktree.update(cx, |worktree, cx| {
1219                worktree.handle_format_buffer(envelope, rpc, cx)
1220            })?;
1221        }
1222        Ok(())
1223    }
1224
1225    pub fn handle_open_buffer(
1226        &mut self,
1227        envelope: TypedEnvelope<proto::OpenBuffer>,
1228        rpc: Arc<Client>,
1229        cx: &mut ModelContext<Self>,
1230    ) -> anyhow::Result<()> {
1231        let receipt = envelope.receipt();
1232        let peer_id = envelope.original_sender_id()?;
1233        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1234        let worktree = self
1235            .worktree_for_id(worktree_id, cx)
1236            .ok_or_else(|| anyhow!("no such worktree"))?;
1237
1238        let task = self.open_buffer(
1239            ProjectPath {
1240                worktree_id,
1241                path: PathBuf::from(envelope.payload.path).into(),
1242            },
1243            cx,
1244        );
1245        cx.spawn(|_, mut cx| {
1246            async move {
1247                let buffer = task.await?;
1248                let response = worktree.update(&mut cx, |worktree, cx| {
1249                    worktree
1250                        .as_local_mut()
1251                        .unwrap()
1252                        .open_remote_buffer(peer_id, buffer, cx)
1253                });
1254                rpc.respond(receipt, response).await?;
1255                Ok(())
1256            }
1257            .log_err()
1258        })
1259        .detach();
1260        Ok(())
1261    }
1262
1263    pub fn handle_close_buffer(
1264        &mut self,
1265        envelope: TypedEnvelope<proto::CloseBuffer>,
1266        _: Arc<Client>,
1267        cx: &mut ModelContext<Self>,
1268    ) -> anyhow::Result<()> {
1269        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1270        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1271            worktree.update(cx, |worktree, cx| {
1272                worktree
1273                    .as_local_mut()
1274                    .unwrap()
1275                    .close_remote_buffer(envelope, cx)
1276            })?;
1277        }
1278        Ok(())
1279    }
1280
1281    pub fn handle_buffer_saved(
1282        &mut self,
1283        envelope: TypedEnvelope<proto::BufferSaved>,
1284        _: Arc<Client>,
1285        cx: &mut ModelContext<Self>,
1286    ) -> Result<()> {
1287        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1288        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1289            worktree.update(cx, |worktree, cx| {
1290                worktree.handle_buffer_saved(envelope, cx)
1291            })?;
1292        }
1293        Ok(())
1294    }
1295
1296    pub fn match_paths<'a>(
1297        &self,
1298        query: &'a str,
1299        include_ignored: bool,
1300        smart_case: bool,
1301        max_results: usize,
1302        cancel_flag: &'a AtomicBool,
1303        cx: &AppContext,
1304    ) -> impl 'a + Future<Output = Vec<PathMatch>> {
1305        let include_root_name = self.worktrees.len() > 1;
1306        let candidate_sets = self
1307            .worktrees
1308            .iter()
1309            .map(|worktree| CandidateSet {
1310                snapshot: worktree.read(cx).snapshot(),
1311                include_ignored,
1312                include_root_name,
1313            })
1314            .collect::<Vec<_>>();
1315
1316        let background = cx.background().clone();
1317        async move {
1318            fuzzy::match_paths(
1319                candidate_sets.as_slice(),
1320                query,
1321                smart_case,
1322                max_results,
1323                cancel_flag,
1324                background,
1325            )
1326            .await
1327        }
1328    }
1329}
1330
1331struct CandidateSet {
1332    snapshot: Snapshot,
1333    include_ignored: bool,
1334    include_root_name: bool,
1335}
1336
1337impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
1338    type Candidates = CandidateSetIter<'a>;
1339
1340    fn id(&self) -> usize {
1341        self.snapshot.id().to_usize()
1342    }
1343
1344    fn len(&self) -> usize {
1345        if self.include_ignored {
1346            self.snapshot.file_count()
1347        } else {
1348            self.snapshot.visible_file_count()
1349        }
1350    }
1351
1352    fn prefix(&self) -> Arc<str> {
1353        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
1354            self.snapshot.root_name().into()
1355        } else if self.include_root_name {
1356            format!("{}/", self.snapshot.root_name()).into()
1357        } else {
1358            "".into()
1359        }
1360    }
1361
1362    fn candidates(&'a self, start: usize) -> Self::Candidates {
1363        CandidateSetIter {
1364            traversal: self.snapshot.files(self.include_ignored, start),
1365        }
1366    }
1367}
1368
1369struct CandidateSetIter<'a> {
1370    traversal: Traversal<'a>,
1371}
1372
1373impl<'a> Iterator for CandidateSetIter<'a> {
1374    type Item = PathMatchCandidate<'a>;
1375
1376    fn next(&mut self) -> Option<Self::Item> {
1377        self.traversal.next().map(|entry| {
1378            if let EntryKind::File(char_bag) = entry.kind {
1379                PathMatchCandidate {
1380                    path: &entry.path,
1381                    char_bag,
1382                }
1383            } else {
1384                unreachable!()
1385            }
1386        })
1387    }
1388}
1389
1390impl Entity for Project {
1391    type Event = Event;
1392
1393    fn release(&mut self, cx: &mut gpui::MutableAppContext) {
1394        match &self.client_state {
1395            ProjectClientState::Local { remote_id_rx, .. } => {
1396                if let Some(project_id) = *remote_id_rx.borrow() {
1397                    let rpc = self.client.clone();
1398                    cx.spawn(|_| async move {
1399                        if let Err(err) = rpc.send(proto::UnregisterProject { project_id }).await {
1400                            log::error!("error unregistering project: {}", err);
1401                        }
1402                    })
1403                    .detach();
1404                }
1405            }
1406            ProjectClientState::Remote { remote_id, .. } => {
1407                let rpc = self.client.clone();
1408                let project_id = *remote_id;
1409                cx.spawn(|_| async move {
1410                    if let Err(err) = rpc.send(proto::LeaveProject { project_id }).await {
1411                        log::error!("error leaving project: {}", err);
1412                    }
1413                })
1414                .detach();
1415            }
1416        }
1417    }
1418
1419    fn app_will_quit(
1420        &mut self,
1421        _: &mut MutableAppContext,
1422    ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
1423        use futures::FutureExt;
1424
1425        let shutdown_futures = self
1426            .language_servers
1427            .drain()
1428            .filter_map(|(_, server)| server.shutdown())
1429            .collect::<Vec<_>>();
1430        Some(
1431            async move {
1432                futures::future::join_all(shutdown_futures).await;
1433            }
1434            .boxed(),
1435        )
1436    }
1437}
1438
1439impl Collaborator {
1440    fn from_proto(
1441        message: proto::Collaborator,
1442        user_store: &ModelHandle<UserStore>,
1443        cx: &mut AsyncAppContext,
1444    ) -> impl Future<Output = Result<Self>> {
1445        let user = user_store.update(cx, |user_store, cx| {
1446            user_store.fetch_user(message.user_id, cx)
1447        });
1448
1449        async move {
1450            Ok(Self {
1451                peer_id: PeerId(message.peer_id),
1452                user: user.await?,
1453                replica_id: message.replica_id as ReplicaId,
1454            })
1455        }
1456    }
1457}
1458
1459#[cfg(test)]
1460mod tests {
1461    use super::{Event, *};
1462    use client::test::FakeHttpClient;
1463    use fs::RealFs;
1464    use futures::StreamExt;
1465    use gpui::{test::subscribe, TestAppContext};
1466    use language::{
1467        tree_sitter_rust, Diagnostic, LanguageConfig, LanguageRegistry, LanguageServerConfig, Point,
1468    };
1469    use lsp::Url;
1470    use serde_json::json;
1471    use std::{os::unix, path::PathBuf};
1472    use util::test::temp_tree;
1473
1474    #[gpui::test]
1475    async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
1476        let dir = temp_tree(json!({
1477            "root": {
1478                "apple": "",
1479                "banana": {
1480                    "carrot": {
1481                        "date": "",
1482                        "endive": "",
1483                    }
1484                },
1485                "fennel": {
1486                    "grape": "",
1487                }
1488            }
1489        }));
1490
1491        let root_link_path = dir.path().join("root_link");
1492        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
1493        unix::fs::symlink(
1494            &dir.path().join("root/fennel"),
1495            &dir.path().join("root/finnochio"),
1496        )
1497        .unwrap();
1498
1499        let project = build_project(&mut cx);
1500
1501        let tree = project
1502            .update(&mut cx, |project, cx| {
1503                project.add_local_worktree(&root_link_path, cx)
1504            })
1505            .await
1506            .unwrap();
1507
1508        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1509            .await;
1510        cx.read(|cx| {
1511            let tree = tree.read(cx);
1512            assert_eq!(tree.file_count(), 5);
1513            assert_eq!(
1514                tree.inode_for_path("fennel/grape"),
1515                tree.inode_for_path("finnochio/grape")
1516            );
1517        });
1518
1519        let cancel_flag = Default::default();
1520        let results = project
1521            .read_with(&cx, |project, cx| {
1522                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
1523            })
1524            .await;
1525        assert_eq!(
1526            results
1527                .into_iter()
1528                .map(|result| result.path)
1529                .collect::<Vec<Arc<Path>>>(),
1530            vec![
1531                PathBuf::from("banana/carrot/date").into(),
1532                PathBuf::from("banana/carrot/endive").into(),
1533            ]
1534        );
1535    }
1536
1537    #[gpui::test]
1538    async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
1539        let (language_server_config, mut fake_server) =
1540            LanguageServerConfig::fake(cx.background()).await;
1541        let progress_token = language_server_config
1542            .disk_based_diagnostics_progress_token
1543            .clone()
1544            .unwrap();
1545
1546        let mut languages = LanguageRegistry::new();
1547        languages.add(Arc::new(Language::new(
1548            LanguageConfig {
1549                name: "Rust".to_string(),
1550                path_suffixes: vec!["rs".to_string()],
1551                language_server: Some(language_server_config),
1552                ..Default::default()
1553            },
1554            Some(tree_sitter_rust::language()),
1555        )));
1556
1557        let dir = temp_tree(json!({
1558            "a.rs": "fn a() { A }",
1559            "b.rs": "const y: i32 = 1",
1560        }));
1561
1562        let http_client = FakeHttpClient::with_404_response();
1563        let client = Client::new(http_client.clone());
1564        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1565
1566        let project = cx.update(|cx| {
1567            Project::local(
1568                client,
1569                user_store,
1570                Arc::new(languages),
1571                Arc::new(RealFs),
1572                cx,
1573            )
1574        });
1575
1576        let tree = project
1577            .update(&mut cx, |project, cx| {
1578                project.add_local_worktree(dir.path(), cx)
1579            })
1580            .await
1581            .unwrap();
1582        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
1583
1584        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1585            .await;
1586
1587        // Cause worktree to start the fake language server
1588        let _buffer = project
1589            .update(&mut cx, |project, cx| {
1590                project.open_buffer(
1591                    ProjectPath {
1592                        worktree_id,
1593                        path: Path::new("b.rs").into(),
1594                    },
1595                    cx,
1596                )
1597            })
1598            .await
1599            .unwrap();
1600
1601        let mut events = subscribe(&project, &mut cx);
1602
1603        fake_server.start_progress(&progress_token).await;
1604        assert_eq!(
1605            events.next().await.unwrap(),
1606            Event::DiskBasedDiagnosticsStarted
1607        );
1608
1609        fake_server.start_progress(&progress_token).await;
1610        fake_server.end_progress(&progress_token).await;
1611        fake_server.start_progress(&progress_token).await;
1612
1613        fake_server
1614            .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
1615                uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
1616                version: None,
1617                diagnostics: vec![lsp::Diagnostic {
1618                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
1619                    severity: Some(lsp::DiagnosticSeverity::ERROR),
1620                    message: "undefined variable 'A'".to_string(),
1621                    ..Default::default()
1622                }],
1623            })
1624            .await;
1625        assert_eq!(
1626            events.next().await.unwrap(),
1627            Event::DiagnosticsUpdated(ProjectPath {
1628                worktree_id,
1629                path: Arc::from(Path::new("a.rs"))
1630            })
1631        );
1632
1633        fake_server.end_progress(&progress_token).await;
1634        fake_server.end_progress(&progress_token).await;
1635        assert_eq!(
1636            events.next().await.unwrap(),
1637            Event::DiskBasedDiagnosticsUpdated
1638        );
1639        assert_eq!(
1640            events.next().await.unwrap(),
1641            Event::DiskBasedDiagnosticsFinished
1642        );
1643
1644        let (buffer, _) = tree
1645            .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))
1646            .await
1647            .unwrap();
1648
1649        buffer.read_with(&cx, |buffer, _| {
1650            let snapshot = buffer.snapshot();
1651            let diagnostics = snapshot
1652                .diagnostics_in_range::<_, Point>(0..buffer.len())
1653                .collect::<Vec<_>>();
1654            assert_eq!(
1655                diagnostics,
1656                &[DiagnosticEntry {
1657                    range: Point::new(0, 9)..Point::new(0, 10),
1658                    diagnostic: Diagnostic {
1659                        severity: lsp::DiagnosticSeverity::ERROR,
1660                        message: "undefined variable 'A'".to_string(),
1661                        group_id: 0,
1662                        is_primary: true,
1663                        ..Default::default()
1664                    }
1665                }]
1666            )
1667        });
1668    }
1669
1670    #[gpui::test]
1671    async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
1672        let dir = temp_tree(json!({
1673            "root": {
1674                "dir1": {},
1675                "dir2": {
1676                    "dir3": {}
1677                }
1678            }
1679        }));
1680
1681        let project = build_project(&mut cx);
1682        let tree = project
1683            .update(&mut cx, |project, cx| {
1684                project.add_local_worktree(&dir.path(), cx)
1685            })
1686            .await
1687            .unwrap();
1688
1689        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1690            .await;
1691
1692        let cancel_flag = Default::default();
1693        let results = project
1694            .read_with(&cx, |project, cx| {
1695                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
1696            })
1697            .await;
1698
1699        assert!(results.is_empty());
1700    }
1701
1702    fn build_project(cx: &mut TestAppContext) -> ModelHandle<Project> {
1703        let languages = Arc::new(LanguageRegistry::new());
1704        let fs = Arc::new(RealFs);
1705        let http_client = FakeHttpClient::with_404_response();
1706        let client = client::Client::new(http_client.clone());
1707        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1708        cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
1709    }
1710}