project.rs

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