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: ProjectPath,
 480        cx: &mut ModelContext<Self>,
 481    ) -> Task<Result<ModelHandle<Buffer>>> {
 482        let worktree = if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
 483            worktree
 484        } else {
 485            return cx.spawn(|_, _| async move { Err(anyhow!("no such worktree")) });
 486        };
 487        let buffer_task = worktree.update(cx, |worktree, cx| worktree.open_buffer(path.path, cx));
 488        cx.spawn(|this, mut cx| async move {
 489            let (buffer, buffer_is_new) = buffer_task.await?;
 490            if buffer_is_new {
 491                this.update(&mut cx, |this, cx| {
 492                    this.assign_language_to_buffer(worktree, buffer.clone(), cx)
 493                });
 494            }
 495            Ok(buffer)
 496        })
 497    }
 498
 499    pub fn save_buffer_as(
 500        &self,
 501        buffer: ModelHandle<Buffer>,
 502        abs_path: PathBuf,
 503        cx: &mut ModelContext<Project>,
 504    ) -> Task<Result<()>> {
 505        let worktree_task = self.find_or_create_worktree_for_abs_path(&abs_path, false, cx);
 506        cx.spawn(|this, mut cx| async move {
 507            let (worktree, path) = worktree_task.await?;
 508            worktree
 509                .update(&mut cx, |worktree, cx| {
 510                    worktree
 511                        .as_local_mut()
 512                        .unwrap()
 513                        .save_buffer_as(buffer.clone(), path, cx)
 514                })
 515                .await?;
 516            this.update(&mut cx, |this, cx| {
 517                this.assign_language_to_buffer(worktree, buffer, cx)
 518            });
 519            Ok(())
 520        })
 521    }
 522
 523    fn assign_language_to_buffer(
 524        &mut self,
 525        worktree: ModelHandle<Worktree>,
 526        buffer: ModelHandle<Buffer>,
 527        cx: &mut ModelContext<Self>,
 528    ) -> Option<()> {
 529        // Set the buffer's language
 530        let full_path = buffer.read(cx).file()?.full_path();
 531        let language = self.languages.select_language(&full_path)?.clone();
 532        buffer.update(cx, |buffer, cx| {
 533            buffer.set_language(Some(language.clone()), cx);
 534        });
 535
 536        // For local worktrees, start a language server if needed.
 537        let worktree = worktree.read(cx);
 538        let worktree_id = worktree.id();
 539        let worktree_abs_path = worktree.as_local()?.abs_path().clone();
 540        let language_server = match self
 541            .language_servers
 542            .entry((worktree_id, language.name().to_string()))
 543        {
 544            hash_map::Entry::Occupied(e) => Some(e.get().clone()),
 545            hash_map::Entry::Vacant(e) => {
 546                Self::start_language_server(self.client.clone(), language, &worktree_abs_path, cx)
 547                    .map(|server| e.insert(server).clone())
 548            }
 549        };
 550
 551        buffer.update(cx, |buffer, cx| {
 552            buffer.set_language_server(language_server, cx)
 553        });
 554
 555        None
 556    }
 557
 558    fn start_language_server(
 559        rpc: Arc<Client>,
 560        language: Arc<Language>,
 561        worktree_path: &Path,
 562        cx: &mut ModelContext<Self>,
 563    ) -> Option<Arc<LanguageServer>> {
 564        enum LspEvent {
 565            DiagnosticsStart,
 566            DiagnosticsUpdate(lsp::PublishDiagnosticsParams),
 567            DiagnosticsFinish,
 568        }
 569
 570        let language_server = language
 571            .start_server(worktree_path, cx)
 572            .log_err()
 573            .flatten()?;
 574        let disk_based_sources = language
 575            .disk_based_diagnostic_sources()
 576            .cloned()
 577            .unwrap_or_default();
 578        let disk_based_diagnostics_progress_token =
 579            language.disk_based_diagnostics_progress_token().cloned();
 580        let has_disk_based_diagnostic_progress_token =
 581            disk_based_diagnostics_progress_token.is_some();
 582        let (diagnostics_tx, diagnostics_rx) = smol::channel::unbounded();
 583
 584        // Listen for `PublishDiagnostics` notifications.
 585        language_server
 586            .on_notification::<lsp::notification::PublishDiagnostics, _>({
 587                let diagnostics_tx = diagnostics_tx.clone();
 588                move |params| {
 589                    if !has_disk_based_diagnostic_progress_token {
 590                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 591                    }
 592                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsUpdate(params))).ok();
 593                    if !has_disk_based_diagnostic_progress_token {
 594                        block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 595                    }
 596                }
 597            })
 598            .detach();
 599
 600        // Listen for `Progress` notifications. Send an event when the language server
 601        // transitions between running jobs and not running any jobs.
 602        let mut running_jobs_for_this_server: i32 = 0;
 603        language_server
 604            .on_notification::<lsp::notification::Progress, _>(move |params| {
 605                let token = match params.token {
 606                    lsp::NumberOrString::Number(_) => None,
 607                    lsp::NumberOrString::String(token) => Some(token),
 608                };
 609
 610                if token == disk_based_diagnostics_progress_token {
 611                    match params.value {
 612                        lsp::ProgressParamsValue::WorkDone(progress) => match progress {
 613                            lsp::WorkDoneProgress::Begin(_) => {
 614                                running_jobs_for_this_server += 1;
 615                                if running_jobs_for_this_server == 1 {
 616                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsStart)).ok();
 617                                }
 618                            }
 619                            lsp::WorkDoneProgress::End(_) => {
 620                                running_jobs_for_this_server -= 1;
 621                                if running_jobs_for_this_server == 0 {
 622                                    block_on(diagnostics_tx.send(LspEvent::DiagnosticsFinish)).ok();
 623                                }
 624                            }
 625                            _ => {}
 626                        },
 627                    }
 628                }
 629            })
 630            .detach();
 631
 632        // Process all the LSP events.
 633        cx.spawn_weak(|this, mut cx| async move {
 634            while let Ok(message) = diagnostics_rx.recv().await {
 635                let this = cx.read(|cx| this.upgrade(cx))?;
 636                match message {
 637                    LspEvent::DiagnosticsStart => {
 638                        let send = this.update(&mut cx, |this, cx| {
 639                            this.disk_based_diagnostics_started(cx);
 640                            this.remote_id().map(|project_id| {
 641                                rpc.send(proto::DiskBasedDiagnosticsUpdating { project_id })
 642                            })
 643                        });
 644                        if let Some(send) = send {
 645                            send.await.log_err();
 646                        }
 647                    }
 648                    LspEvent::DiagnosticsUpdate(params) => {
 649                        this.update(&mut cx, |this, cx| {
 650                            this.update_diagnostics(params, &disk_based_sources, cx)
 651                                .log_err();
 652                        });
 653                    }
 654                    LspEvent::DiagnosticsFinish => {
 655                        let send = this.update(&mut cx, |this, cx| {
 656                            this.disk_based_diagnostics_finished(cx);
 657                            this.remote_id().map(|project_id| {
 658                                rpc.send(proto::DiskBasedDiagnosticsUpdated { project_id })
 659                            })
 660                        });
 661                        if let Some(send) = send {
 662                            send.await.log_err();
 663                        }
 664                    }
 665                }
 666            }
 667            Some(())
 668        })
 669        .detach();
 670
 671        Some(language_server)
 672    }
 673
 674    fn update_diagnostics(
 675        &mut self,
 676        diagnostics: lsp::PublishDiagnosticsParams,
 677        disk_based_sources: &HashSet<String>,
 678        cx: &mut ModelContext<Self>,
 679    ) -> Result<()> {
 680        let path = diagnostics
 681            .uri
 682            .to_file_path()
 683            .map_err(|_| anyhow!("URI is not a file"))?;
 684        let (worktree, relative_path) = self
 685            .find_worktree_for_abs_path(&path, cx)
 686            .ok_or_else(|| anyhow!("no worktree found for diagnostics"))?;
 687        let project_path = ProjectPath {
 688            worktree_id: worktree.read(cx).id(),
 689            path: relative_path.into(),
 690        };
 691        worktree.update(cx, |worktree, cx| {
 692            worktree.as_local_mut().unwrap().update_diagnostics(
 693                project_path.path.clone(),
 694                diagnostics,
 695                disk_based_sources,
 696                cx,
 697            )
 698        })?;
 699        cx.emit(Event::DiagnosticsUpdated(project_path));
 700        Ok(())
 701    }
 702
 703    pub fn definition<T: ToOffset>(
 704        &self,
 705        source_buffer_handle: &ModelHandle<Buffer>,
 706        position: T,
 707        cx: &mut ModelContext<Self>,
 708    ) -> Task<Result<Vec<Definition>>> {
 709        let source_buffer_handle = source_buffer_handle.clone();
 710        let buffer = source_buffer_handle.read(cx);
 711        let worktree;
 712        let buffer_abs_path;
 713        if let Some(file) = File::from_dyn(buffer.file()) {
 714            worktree = file.worktree.clone();
 715            buffer_abs_path = file.abs_path();
 716        } else {
 717            return Task::ready(Err(anyhow!("buffer does not belong to any worktree")));
 718        };
 719
 720        if worktree.read(cx).as_local().is_some() {
 721            let point = buffer.offset_to_point_utf16(position.to_offset(buffer));
 722            let buffer_abs_path = buffer_abs_path.unwrap();
 723            let lang_name;
 724            let lang_server;
 725            if let Some(lang) = buffer.language() {
 726                lang_name = lang.name().to_string();
 727                if let Some(server) = self
 728                    .language_servers
 729                    .get(&(worktree.read(cx).id(), lang_name.clone()))
 730                {
 731                    lang_server = server.clone();
 732                } else {
 733                    return Task::ready(Err(anyhow!("buffer does not have a language server")));
 734                };
 735            } else {
 736                return Task::ready(Err(anyhow!("buffer does not have a language")));
 737            }
 738
 739            cx.spawn(|this, mut cx| async move {
 740                let response = lang_server
 741                    .request::<lsp::request::GotoDefinition>(lsp::GotoDefinitionParams {
 742                        text_document_position_params: lsp::TextDocumentPositionParams {
 743                            text_document: lsp::TextDocumentIdentifier::new(
 744                                lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
 745                            ),
 746                            position: lsp::Position::new(point.row, point.column),
 747                        },
 748                        work_done_progress_params: Default::default(),
 749                        partial_result_params: Default::default(),
 750                    })
 751                    .await?;
 752
 753                let mut definitions = Vec::new();
 754                if let Some(response) = response {
 755                    let mut unresolved_locations = Vec::new();
 756                    match response {
 757                        lsp::GotoDefinitionResponse::Scalar(loc) => {
 758                            unresolved_locations.push((loc.uri, loc.range));
 759                        }
 760                        lsp::GotoDefinitionResponse::Array(locs) => {
 761                            unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
 762                        }
 763                        lsp::GotoDefinitionResponse::Link(links) => {
 764                            unresolved_locations.extend(
 765                                links
 766                                    .into_iter()
 767                                    .map(|l| (l.target_uri, l.target_selection_range)),
 768                            );
 769                        }
 770                    }
 771
 772                    for (target_uri, target_range) in unresolved_locations {
 773                        let abs_path = target_uri
 774                            .to_file_path()
 775                            .map_err(|_| anyhow!("invalid target path"))?;
 776
 777                        let (worktree, relative_path) = if let Some(result) = this
 778                            .read_with(&cx, |this, cx| {
 779                                this.find_worktree_for_abs_path(&abs_path, cx)
 780                            }) {
 781                            result
 782                        } else {
 783                            let (worktree, relative_path) = this
 784                                .update(&mut cx, |this, cx| {
 785                                    this.create_worktree_for_abs_path(&abs_path, true, cx)
 786                                })
 787                                .await?;
 788                            this.update(&mut cx, |this, cx| {
 789                                this.language_servers.insert(
 790                                    (worktree.read(cx).id(), lang_name.clone()),
 791                                    lang_server.clone(),
 792                                );
 793                            });
 794                            (worktree, relative_path)
 795                        };
 796
 797                        let project_path = ProjectPath {
 798                            worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
 799                            path: relative_path.into(),
 800                        };
 801                        let target_buffer_handle = this
 802                            .update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
 803                            .await?;
 804                        cx.read(|cx| {
 805                            let target_buffer = target_buffer_handle.read(cx);
 806                            let target_start = target_buffer
 807                                .clip_point_utf16(target_range.start.to_point_utf16(), Bias::Left);
 808                            let target_end = target_buffer
 809                                .clip_point_utf16(target_range.end.to_point_utf16(), Bias::Left);
 810                            definitions.push(Definition {
 811                                target_buffer: target_buffer_handle,
 812                                target_range: target_buffer.anchor_after(target_start)
 813                                    ..target_buffer.anchor_before(target_end),
 814                            });
 815                        });
 816                    }
 817                }
 818
 819                Ok(definitions)
 820            })
 821        } else {
 822            todo!()
 823        }
 824    }
 825
 826    pub fn find_or_create_worktree_for_abs_path(
 827        &self,
 828        abs_path: impl AsRef<Path>,
 829        weak: bool,
 830        cx: &mut ModelContext<Self>,
 831    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 832        let abs_path = abs_path.as_ref();
 833        if let Some((tree, relative_path)) = self.find_worktree_for_abs_path(abs_path, cx) {
 834            Task::ready(Ok((tree.clone(), relative_path.into())))
 835        } else {
 836            self.create_worktree_for_abs_path(abs_path, weak, cx)
 837        }
 838    }
 839
 840    fn create_worktree_for_abs_path(
 841        &self,
 842        abs_path: &Path,
 843        weak: bool,
 844        cx: &mut ModelContext<Self>,
 845    ) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
 846        let worktree = self.add_local_worktree(abs_path, weak, cx);
 847        cx.background().spawn(async move {
 848            let worktree = worktree.await?;
 849            Ok((worktree, PathBuf::new()))
 850        })
 851    }
 852
 853    fn find_worktree_for_abs_path(
 854        &self,
 855        abs_path: &Path,
 856        cx: &AppContext,
 857    ) -> Option<(ModelHandle<Worktree>, PathBuf)> {
 858        for tree in self.worktrees(cx) {
 859            if let Some(relative_path) = tree
 860                .read(cx)
 861                .as_local()
 862                .and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
 863            {
 864                return Some((tree.clone(), relative_path.into()));
 865            }
 866        }
 867        None
 868    }
 869
 870    pub fn is_shared(&self) -> bool {
 871        match &self.client_state {
 872            ProjectClientState::Local { is_shared, .. } => *is_shared,
 873            ProjectClientState::Remote { .. } => false,
 874        }
 875    }
 876
 877    fn add_local_worktree(
 878        &self,
 879        abs_path: impl AsRef<Path>,
 880        weak: bool,
 881        cx: &mut ModelContext<Self>,
 882    ) -> Task<Result<ModelHandle<Worktree>>> {
 883        let fs = self.fs.clone();
 884        let client = self.client.clone();
 885        let user_store = self.user_store.clone();
 886        let path = Arc::from(abs_path.as_ref());
 887        cx.spawn(|project, mut cx| async move {
 888            let worktree =
 889                Worktree::open_local(client.clone(), user_store, path, weak, fs, &mut cx).await?;
 890
 891            let (remote_project_id, is_shared) = project.update(&mut cx, |project, cx| {
 892                project.add_worktree(&worktree, cx);
 893                (project.remote_id(), project.is_shared())
 894            });
 895
 896            if let Some(project_id) = remote_project_id {
 897                worktree
 898                    .update(&mut cx, |worktree, cx| {
 899                        worktree.as_local_mut().unwrap().register(project_id, cx)
 900                    })
 901                    .await?;
 902                if is_shared {
 903                    worktree
 904                        .update(&mut cx, |worktree, cx| {
 905                            worktree.as_local_mut().unwrap().share(cx)
 906                        })
 907                        .await?;
 908                }
 909            }
 910
 911            Ok(worktree)
 912        })
 913    }
 914
 915    pub fn remove_worktree(&mut self, id: WorktreeId, cx: &mut ModelContext<Self>) {
 916        self.worktrees.retain(|worktree| {
 917            worktree
 918                .upgrade(cx)
 919                .map_or(false, |w| w.read(cx).id() != id)
 920        });
 921        cx.notify();
 922    }
 923
 924    fn add_worktree(&mut self, worktree: &ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
 925        cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
 926
 927        let push_weak_handle = {
 928            let worktree = worktree.read(cx);
 929            worktree.is_local() && worktree.is_weak()
 930        };
 931        if push_weak_handle {
 932            cx.observe_release(&worktree, |this, cx| {
 933                this.worktrees
 934                    .retain(|worktree| worktree.upgrade(cx).is_some());
 935                cx.notify();
 936            })
 937            .detach();
 938            self.worktrees
 939                .push(WorktreeHandle::Weak(worktree.downgrade()));
 940        } else {
 941            self.worktrees
 942                .push(WorktreeHandle::Strong(worktree.clone()));
 943        }
 944        cx.notify();
 945    }
 946
 947    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
 948        let new_active_entry = entry.and_then(|project_path| {
 949            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
 950            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
 951            Some(ProjectEntry {
 952                worktree_id: project_path.worktree_id,
 953                entry_id: entry.id,
 954            })
 955        });
 956        if new_active_entry != self.active_entry {
 957            self.active_entry = new_active_entry;
 958            cx.emit(Event::ActiveEntryChanged(new_active_entry));
 959        }
 960    }
 961
 962    pub fn is_running_disk_based_diagnostics(&self) -> bool {
 963        self.language_servers_with_diagnostics_running > 0
 964    }
 965
 966    pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
 967        let mut summary = DiagnosticSummary::default();
 968        for (_, path_summary) in self.diagnostic_summaries(cx) {
 969            summary.error_count += path_summary.error_count;
 970            summary.warning_count += path_summary.warning_count;
 971            summary.info_count += path_summary.info_count;
 972            summary.hint_count += path_summary.hint_count;
 973        }
 974        summary
 975    }
 976
 977    pub fn diagnostic_summaries<'a>(
 978        &'a self,
 979        cx: &'a AppContext,
 980    ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
 981        self.worktrees(cx).flat_map(move |worktree| {
 982            let worktree = worktree.read(cx);
 983            let worktree_id = worktree.id();
 984            worktree
 985                .diagnostic_summaries()
 986                .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
 987        })
 988    }
 989
 990    fn disk_based_diagnostics_started(&mut self, cx: &mut ModelContext<Self>) {
 991        self.language_servers_with_diagnostics_running += 1;
 992        if self.language_servers_with_diagnostics_running == 1 {
 993            cx.emit(Event::DiskBasedDiagnosticsStarted);
 994        }
 995    }
 996
 997    fn disk_based_diagnostics_finished(&mut self, cx: &mut ModelContext<Self>) {
 998        cx.emit(Event::DiskBasedDiagnosticsUpdated);
 999        self.language_servers_with_diagnostics_running -= 1;
1000        if self.language_servers_with_diagnostics_running == 0 {
1001            cx.emit(Event::DiskBasedDiagnosticsFinished);
1002        }
1003    }
1004
1005    pub fn active_entry(&self) -> Option<ProjectEntry> {
1006        self.active_entry
1007    }
1008
1009    // RPC message handlers
1010
1011    fn handle_unshare_project(
1012        &mut self,
1013        _: TypedEnvelope<proto::UnshareProject>,
1014        _: Arc<Client>,
1015        cx: &mut ModelContext<Self>,
1016    ) -> Result<()> {
1017        if let ProjectClientState::Remote {
1018            sharing_has_stopped,
1019            ..
1020        } = &mut self.client_state
1021        {
1022            *sharing_has_stopped = true;
1023            self.collaborators.clear();
1024            cx.notify();
1025            Ok(())
1026        } else {
1027            unreachable!()
1028        }
1029    }
1030
1031    fn handle_add_collaborator(
1032        &mut self,
1033        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
1034        _: Arc<Client>,
1035        cx: &mut ModelContext<Self>,
1036    ) -> Result<()> {
1037        let user_store = self.user_store.clone();
1038        let collaborator = envelope
1039            .payload
1040            .collaborator
1041            .take()
1042            .ok_or_else(|| anyhow!("empty collaborator"))?;
1043
1044        cx.spawn(|this, mut cx| {
1045            async move {
1046                let collaborator =
1047                    Collaborator::from_proto(collaborator, &user_store, &mut cx).await?;
1048                this.update(&mut cx, |this, cx| {
1049                    this.collaborators
1050                        .insert(collaborator.peer_id, collaborator);
1051                    cx.notify();
1052                });
1053                Ok(())
1054            }
1055            .log_err()
1056        })
1057        .detach();
1058
1059        Ok(())
1060    }
1061
1062    fn handle_remove_collaborator(
1063        &mut self,
1064        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
1065        _: Arc<Client>,
1066        cx: &mut ModelContext<Self>,
1067    ) -> Result<()> {
1068        let peer_id = PeerId(envelope.payload.peer_id);
1069        let replica_id = self
1070            .collaborators
1071            .remove(&peer_id)
1072            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
1073            .replica_id;
1074        for worktree in self.worktrees(cx).collect::<Vec<_>>() {
1075            worktree.update(cx, |worktree, cx| {
1076                worktree.remove_collaborator(peer_id, replica_id, cx);
1077            })
1078        }
1079        Ok(())
1080    }
1081
1082    fn handle_share_worktree(
1083        &mut self,
1084        envelope: TypedEnvelope<proto::ShareWorktree>,
1085        client: Arc<Client>,
1086        cx: &mut ModelContext<Self>,
1087    ) -> Result<()> {
1088        let remote_id = self.remote_id().ok_or_else(|| anyhow!("invalid project"))?;
1089        let replica_id = self.replica_id();
1090        let worktree = envelope
1091            .payload
1092            .worktree
1093            .ok_or_else(|| anyhow!("invalid worktree"))?;
1094        let user_store = self.user_store.clone();
1095        cx.spawn(|this, mut cx| {
1096            async move {
1097                let worktree =
1098                    Worktree::remote(remote_id, replica_id, worktree, client, user_store, &mut cx)
1099                        .await?;
1100                this.update(&mut cx, |this, cx| this.add_worktree(&worktree, cx));
1101                Ok(())
1102            }
1103            .log_err()
1104        })
1105        .detach();
1106        Ok(())
1107    }
1108
1109    fn handle_unregister_worktree(
1110        &mut self,
1111        envelope: TypedEnvelope<proto::UnregisterWorktree>,
1112        _: Arc<Client>,
1113        cx: &mut ModelContext<Self>,
1114    ) -> Result<()> {
1115        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1116        self.remove_worktree(worktree_id, cx);
1117        Ok(())
1118    }
1119
1120    fn handle_update_worktree(
1121        &mut self,
1122        envelope: TypedEnvelope<proto::UpdateWorktree>,
1123        _: Arc<Client>,
1124        cx: &mut ModelContext<Self>,
1125    ) -> Result<()> {
1126        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1127        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1128            worktree.update(cx, |worktree, cx| {
1129                let worktree = worktree.as_remote_mut().unwrap();
1130                worktree.update_from_remote(envelope, cx)
1131            })?;
1132        }
1133        Ok(())
1134    }
1135
1136    fn handle_update_diagnostic_summary(
1137        &mut self,
1138        envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
1139        _: Arc<Client>,
1140        cx: &mut ModelContext<Self>,
1141    ) -> Result<()> {
1142        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1143        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1144            if let Some(summary) = envelope.payload.summary {
1145                let project_path = ProjectPath {
1146                    worktree_id,
1147                    path: Path::new(&summary.path).into(),
1148                };
1149                worktree.update(cx, |worktree, _| {
1150                    worktree
1151                        .as_remote_mut()
1152                        .unwrap()
1153                        .update_diagnostic_summary(project_path.path.clone(), &summary);
1154                });
1155                cx.emit(Event::DiagnosticsUpdated(project_path));
1156            }
1157        }
1158        Ok(())
1159    }
1160
1161    fn handle_disk_based_diagnostics_updating(
1162        &mut self,
1163        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
1164        _: Arc<Client>,
1165        cx: &mut ModelContext<Self>,
1166    ) -> Result<()> {
1167        self.disk_based_diagnostics_started(cx);
1168        Ok(())
1169    }
1170
1171    fn handle_disk_based_diagnostics_updated(
1172        &mut self,
1173        _: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,
1174        _: Arc<Client>,
1175        cx: &mut ModelContext<Self>,
1176    ) -> Result<()> {
1177        self.disk_based_diagnostics_finished(cx);
1178        Ok(())
1179    }
1180
1181    pub fn handle_update_buffer(
1182        &mut self,
1183        envelope: TypedEnvelope<proto::UpdateBuffer>,
1184        _: Arc<Client>,
1185        cx: &mut ModelContext<Self>,
1186    ) -> Result<()> {
1187        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1188        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1189            worktree.update(cx, |worktree, cx| {
1190                worktree.handle_update_buffer(envelope, cx)
1191            })?;
1192        }
1193        Ok(())
1194    }
1195
1196    pub fn handle_save_buffer(
1197        &mut self,
1198        envelope: TypedEnvelope<proto::SaveBuffer>,
1199        rpc: Arc<Client>,
1200        cx: &mut ModelContext<Self>,
1201    ) -> Result<()> {
1202        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1203        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1204            worktree.update(cx, |worktree, cx| {
1205                worktree.handle_save_buffer(envelope, rpc, cx)
1206            })?;
1207        }
1208        Ok(())
1209    }
1210
1211    pub fn handle_format_buffer(
1212        &mut self,
1213        envelope: TypedEnvelope<proto::FormatBuffer>,
1214        rpc: Arc<Client>,
1215        cx: &mut ModelContext<Self>,
1216    ) -> Result<()> {
1217        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1218        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1219            worktree.update(cx, |worktree, cx| {
1220                worktree.handle_format_buffer(envelope, rpc, cx)
1221            })?;
1222        }
1223        Ok(())
1224    }
1225
1226    pub fn handle_open_buffer(
1227        &mut self,
1228        envelope: TypedEnvelope<proto::OpenBuffer>,
1229        rpc: Arc<Client>,
1230        cx: &mut ModelContext<Self>,
1231    ) -> anyhow::Result<()> {
1232        let receipt = envelope.receipt();
1233        let peer_id = envelope.original_sender_id()?;
1234        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1235        let worktree = self
1236            .worktree_for_id(worktree_id, cx)
1237            .ok_or_else(|| anyhow!("no such worktree"))?;
1238
1239        let task = self.open_buffer(
1240            ProjectPath {
1241                worktree_id,
1242                path: PathBuf::from(envelope.payload.path).into(),
1243            },
1244            cx,
1245        );
1246        cx.spawn(|_, mut cx| {
1247            async move {
1248                let buffer = task.await?;
1249                let response = worktree.update(&mut cx, |worktree, cx| {
1250                    worktree
1251                        .as_local_mut()
1252                        .unwrap()
1253                        .open_remote_buffer(peer_id, buffer, cx)
1254                });
1255                rpc.respond(receipt, response).await?;
1256                Ok(())
1257            }
1258            .log_err()
1259        })
1260        .detach();
1261        Ok(())
1262    }
1263
1264    pub fn handle_close_buffer(
1265        &mut self,
1266        envelope: TypedEnvelope<proto::CloseBuffer>,
1267        _: Arc<Client>,
1268        cx: &mut ModelContext<Self>,
1269    ) -> anyhow::Result<()> {
1270        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1271        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1272            worktree.update(cx, |worktree, cx| {
1273                worktree
1274                    .as_local_mut()
1275                    .unwrap()
1276                    .close_remote_buffer(envelope, cx)
1277            })?;
1278        }
1279        Ok(())
1280    }
1281
1282    pub fn handle_buffer_saved(
1283        &mut self,
1284        envelope: TypedEnvelope<proto::BufferSaved>,
1285        _: Arc<Client>,
1286        cx: &mut ModelContext<Self>,
1287    ) -> Result<()> {
1288        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
1289        if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
1290            worktree.update(cx, |worktree, cx| {
1291                worktree.handle_buffer_saved(envelope, cx)
1292            })?;
1293        }
1294        Ok(())
1295    }
1296
1297    pub fn match_paths<'a>(
1298        &self,
1299        query: &'a str,
1300        include_ignored: bool,
1301        smart_case: bool,
1302        max_results: usize,
1303        cancel_flag: &'a AtomicBool,
1304        cx: &AppContext,
1305    ) -> impl 'a + Future<Output = Vec<PathMatch>> {
1306        let worktrees = self
1307            .worktrees(cx)
1308            .filter(|worktree| !worktree.read(cx).is_weak())
1309            .collect::<Vec<_>>();
1310        let include_root_name = worktrees.len() > 1;
1311        let candidate_sets = worktrees
1312            .into_iter()
1313            .map(|worktree| CandidateSet {
1314                snapshot: worktree.read(cx).snapshot(),
1315                include_ignored,
1316                include_root_name,
1317            })
1318            .collect::<Vec<_>>();
1319
1320        let background = cx.background().clone();
1321        async move {
1322            fuzzy::match_paths(
1323                candidate_sets.as_slice(),
1324                query,
1325                smart_case,
1326                max_results,
1327                cancel_flag,
1328                background,
1329            )
1330            .await
1331        }
1332    }
1333}
1334
1335impl WorktreeHandle {
1336    pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Worktree>> {
1337        match self {
1338            WorktreeHandle::Strong(handle) => Some(handle.clone()),
1339            WorktreeHandle::Weak(handle) => handle.upgrade(cx),
1340        }
1341    }
1342}
1343
1344struct CandidateSet {
1345    snapshot: Snapshot,
1346    include_ignored: bool,
1347    include_root_name: bool,
1348}
1349
1350impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
1351    type Candidates = CandidateSetIter<'a>;
1352
1353    fn id(&self) -> usize {
1354        self.snapshot.id().to_usize()
1355    }
1356
1357    fn len(&self) -> usize {
1358        if self.include_ignored {
1359            self.snapshot.file_count()
1360        } else {
1361            self.snapshot.visible_file_count()
1362        }
1363    }
1364
1365    fn prefix(&self) -> Arc<str> {
1366        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
1367            self.snapshot.root_name().into()
1368        } else if self.include_root_name {
1369            format!("{}/", self.snapshot.root_name()).into()
1370        } else {
1371            "".into()
1372        }
1373    }
1374
1375    fn candidates(&'a self, start: usize) -> Self::Candidates {
1376        CandidateSetIter {
1377            traversal: self.snapshot.files(self.include_ignored, start),
1378        }
1379    }
1380}
1381
1382struct CandidateSetIter<'a> {
1383    traversal: Traversal<'a>,
1384}
1385
1386impl<'a> Iterator for CandidateSetIter<'a> {
1387    type Item = PathMatchCandidate<'a>;
1388
1389    fn next(&mut self) -> Option<Self::Item> {
1390        self.traversal.next().map(|entry| {
1391            if let EntryKind::File(char_bag) = entry.kind {
1392                PathMatchCandidate {
1393                    path: &entry.path,
1394                    char_bag,
1395                }
1396            } else {
1397                unreachable!()
1398            }
1399        })
1400    }
1401}
1402
1403impl Entity for Project {
1404    type Event = Event;
1405
1406    fn release(&mut self, cx: &mut gpui::MutableAppContext) {
1407        match &self.client_state {
1408            ProjectClientState::Local { remote_id_rx, .. } => {
1409                if let Some(project_id) = *remote_id_rx.borrow() {
1410                    let rpc = self.client.clone();
1411                    cx.spawn(|_| async move {
1412                        if let Err(err) = rpc.send(proto::UnregisterProject { project_id }).await {
1413                            log::error!("error unregistering project: {}", err);
1414                        }
1415                    })
1416                    .detach();
1417                }
1418            }
1419            ProjectClientState::Remote { remote_id, .. } => {
1420                let rpc = self.client.clone();
1421                let project_id = *remote_id;
1422                cx.spawn(|_| async move {
1423                    if let Err(err) = rpc.send(proto::LeaveProject { project_id }).await {
1424                        log::error!("error leaving project: {}", err);
1425                    }
1426                })
1427                .detach();
1428            }
1429        }
1430    }
1431
1432    fn app_will_quit(
1433        &mut self,
1434        _: &mut MutableAppContext,
1435    ) -> Option<std::pin::Pin<Box<dyn 'static + Future<Output = ()>>>> {
1436        use futures::FutureExt;
1437
1438        let shutdown_futures = self
1439            .language_servers
1440            .drain()
1441            .filter_map(|(_, server)| server.shutdown())
1442            .collect::<Vec<_>>();
1443        Some(
1444            async move {
1445                futures::future::join_all(shutdown_futures).await;
1446            }
1447            .boxed(),
1448        )
1449    }
1450}
1451
1452impl Collaborator {
1453    fn from_proto(
1454        message: proto::Collaborator,
1455        user_store: &ModelHandle<UserStore>,
1456        cx: &mut AsyncAppContext,
1457    ) -> impl Future<Output = Result<Self>> {
1458        let user = user_store.update(cx, |user_store, cx| {
1459            user_store.fetch_user(message.user_id, cx)
1460        });
1461
1462        async move {
1463            Ok(Self {
1464                peer_id: PeerId(message.peer_id),
1465                user: user.await?,
1466                replica_id: message.replica_id as ReplicaId,
1467            })
1468        }
1469    }
1470}
1471
1472#[cfg(test)]
1473mod tests {
1474    use super::{Event, *};
1475    use client::test::FakeHttpClient;
1476    use fs::RealFs;
1477    use futures::StreamExt;
1478    use gpui::{test::subscribe, TestAppContext};
1479    use language::{
1480        tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageRegistry,
1481        LanguageServerConfig, Point,
1482    };
1483    use lsp::Url;
1484    use serde_json::json;
1485    use std::{os::unix, path::PathBuf};
1486    use util::test::temp_tree;
1487
1488    #[gpui::test]
1489    async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
1490        let dir = temp_tree(json!({
1491            "root": {
1492                "apple": "",
1493                "banana": {
1494                    "carrot": {
1495                        "date": "",
1496                        "endive": "",
1497                    }
1498                },
1499                "fennel": {
1500                    "grape": "",
1501                }
1502            }
1503        }));
1504
1505        let root_link_path = dir.path().join("root_link");
1506        unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
1507        unix::fs::symlink(
1508            &dir.path().join("root/fennel"),
1509            &dir.path().join("root/finnochio"),
1510        )
1511        .unwrap();
1512
1513        let project = build_project(&mut cx);
1514
1515        let (tree, _) = project
1516            .update(&mut cx, |project, cx| {
1517                project.find_or_create_worktree_for_abs_path(&root_link_path, false, cx)
1518            })
1519            .await
1520            .unwrap();
1521
1522        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1523            .await;
1524        cx.read(|cx| {
1525            let tree = tree.read(cx);
1526            assert_eq!(tree.file_count(), 5);
1527            assert_eq!(
1528                tree.inode_for_path("fennel/grape"),
1529                tree.inode_for_path("finnochio/grape")
1530            );
1531        });
1532
1533        let cancel_flag = Default::default();
1534        let results = project
1535            .read_with(&cx, |project, cx| {
1536                project.match_paths("bna", false, false, 10, &cancel_flag, cx)
1537            })
1538            .await;
1539        assert_eq!(
1540            results
1541                .into_iter()
1542                .map(|result| result.path)
1543                .collect::<Vec<Arc<Path>>>(),
1544            vec![
1545                PathBuf::from("banana/carrot/date").into(),
1546                PathBuf::from("banana/carrot/endive").into(),
1547            ]
1548        );
1549    }
1550
1551    #[gpui::test]
1552    async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
1553        let (language_server_config, mut fake_server) =
1554            LanguageServerConfig::fake(cx.background()).await;
1555        let progress_token = language_server_config
1556            .disk_based_diagnostics_progress_token
1557            .clone()
1558            .unwrap();
1559
1560        let mut languages = LanguageRegistry::new();
1561        languages.add(Arc::new(Language::new(
1562            LanguageConfig {
1563                name: "Rust".to_string(),
1564                path_suffixes: vec!["rs".to_string()],
1565                language_server: Some(language_server_config),
1566                ..Default::default()
1567            },
1568            Some(tree_sitter_rust::language()),
1569        )));
1570
1571        let dir = temp_tree(json!({
1572            "a.rs": "fn a() { A }",
1573            "b.rs": "const y: i32 = 1",
1574        }));
1575
1576        let http_client = FakeHttpClient::with_404_response();
1577        let client = Client::new(http_client.clone());
1578        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1579
1580        let project = cx.update(|cx| {
1581            Project::local(
1582                client,
1583                user_store,
1584                Arc::new(languages),
1585                Arc::new(RealFs),
1586                cx,
1587            )
1588        });
1589
1590        let (tree, _) = project
1591            .update(&mut cx, |project, cx| {
1592                project.find_or_create_worktree_for_abs_path(dir.path(), false, cx)
1593            })
1594            .await
1595            .unwrap();
1596        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
1597
1598        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1599            .await;
1600
1601        // Cause worktree to start the fake language server
1602        let _buffer = project
1603            .update(&mut cx, |project, cx| {
1604                project.open_buffer(
1605                    ProjectPath {
1606                        worktree_id,
1607                        path: Path::new("b.rs").into(),
1608                    },
1609                    cx,
1610                )
1611            })
1612            .await
1613            .unwrap();
1614
1615        let mut events = subscribe(&project, &mut cx);
1616
1617        fake_server.start_progress(&progress_token).await;
1618        assert_eq!(
1619            events.next().await.unwrap(),
1620            Event::DiskBasedDiagnosticsStarted
1621        );
1622
1623        fake_server.start_progress(&progress_token).await;
1624        fake_server.end_progress(&progress_token).await;
1625        fake_server.start_progress(&progress_token).await;
1626
1627        fake_server
1628            .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
1629                uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
1630                version: None,
1631                diagnostics: vec![lsp::Diagnostic {
1632                    range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
1633                    severity: Some(lsp::DiagnosticSeverity::ERROR),
1634                    message: "undefined variable 'A'".to_string(),
1635                    ..Default::default()
1636                }],
1637            })
1638            .await;
1639        assert_eq!(
1640            events.next().await.unwrap(),
1641            Event::DiagnosticsUpdated(ProjectPath {
1642                worktree_id,
1643                path: Arc::from(Path::new("a.rs"))
1644            })
1645        );
1646
1647        fake_server.end_progress(&progress_token).await;
1648        fake_server.end_progress(&progress_token).await;
1649        assert_eq!(
1650            events.next().await.unwrap(),
1651            Event::DiskBasedDiagnosticsUpdated
1652        );
1653        assert_eq!(
1654            events.next().await.unwrap(),
1655            Event::DiskBasedDiagnosticsFinished
1656        );
1657
1658        let (buffer, _) = tree
1659            .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))
1660            .await
1661            .unwrap();
1662
1663        buffer.read_with(&cx, |buffer, _| {
1664            let snapshot = buffer.snapshot();
1665            let diagnostics = snapshot
1666                .diagnostics_in_range::<_, Point>(0..buffer.len())
1667                .collect::<Vec<_>>();
1668            assert_eq!(
1669                diagnostics,
1670                &[DiagnosticEntry {
1671                    range: Point::new(0, 9)..Point::new(0, 10),
1672                    diagnostic: Diagnostic {
1673                        severity: lsp::DiagnosticSeverity::ERROR,
1674                        message: "undefined variable 'A'".to_string(),
1675                        group_id: 0,
1676                        is_primary: true,
1677                        ..Default::default()
1678                    }
1679                }]
1680            )
1681        });
1682    }
1683
1684    #[gpui::test]
1685    async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
1686        let dir = temp_tree(json!({
1687            "root": {
1688                "dir1": {},
1689                "dir2": {
1690                    "dir3": {}
1691                }
1692            }
1693        }));
1694
1695        let project = build_project(&mut cx);
1696        let (tree, _) = project
1697            .update(&mut cx, |project, cx| {
1698                project.find_or_create_worktree_for_abs_path(&dir.path(), false, cx)
1699            })
1700            .await
1701            .unwrap();
1702
1703        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1704            .await;
1705
1706        let cancel_flag = Default::default();
1707        let results = project
1708            .read_with(&cx, |project, cx| {
1709                project.match_paths("dir", false, false, 10, &cancel_flag, cx)
1710            })
1711            .await;
1712
1713        assert!(results.is_empty());
1714    }
1715
1716    #[gpui::test]
1717    async fn test_definition(mut cx: gpui::TestAppContext) {
1718        let (language_server_config, mut fake_server) =
1719            LanguageServerConfig::fake(cx.background()).await;
1720
1721        let mut languages = LanguageRegistry::new();
1722        languages.add(Arc::new(Language::new(
1723            LanguageConfig {
1724                name: "Rust".to_string(),
1725                path_suffixes: vec!["rs".to_string()],
1726                language_server: Some(language_server_config),
1727                ..Default::default()
1728            },
1729            Some(tree_sitter_rust::language()),
1730        )));
1731
1732        let dir = temp_tree(json!({
1733            "a.rs": "const fn a() { A }",
1734            "b.rs": "const y: i32 = crate::a()",
1735        }));
1736
1737        let http_client = FakeHttpClient::with_404_response();
1738        let client = Client::new(http_client.clone());
1739        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1740        let project = cx.update(|cx| {
1741            Project::local(
1742                client,
1743                user_store,
1744                Arc::new(languages),
1745                Arc::new(RealFs),
1746                cx,
1747            )
1748        });
1749
1750        let (tree, _) = project
1751            .update(&mut cx, |project, cx| {
1752                project.find_or_create_worktree_for_abs_path(dir.path().join("b.rs"), false, cx)
1753            })
1754            .await
1755            .unwrap();
1756        let worktree_id = tree.read_with(&cx, |tree, _| tree.id());
1757        cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
1758            .await;
1759
1760        // Cause worktree to start the fake language server
1761        let buffer = project
1762            .update(&mut cx, |project, cx| {
1763                project.open_buffer(
1764                    ProjectPath {
1765                        worktree_id,
1766                        path: Path::new("").into(),
1767                    },
1768                    cx,
1769                )
1770            })
1771            .await
1772            .unwrap();
1773        let definitions =
1774            project.update(&mut cx, |project, cx| project.definition(&buffer, 22, cx));
1775        let (request_id, request) = fake_server
1776            .receive_request::<lsp::request::GotoDefinition>()
1777            .await;
1778        let request_params = request.text_document_position_params;
1779        assert_eq!(
1780            request_params.text_document.uri.to_file_path().unwrap(),
1781            dir.path().join("b.rs")
1782        );
1783        assert_eq!(request_params.position, lsp::Position::new(0, 22));
1784
1785        fake_server
1786            .respond(
1787                request_id,
1788                Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new(
1789                    lsp::Url::from_file_path(dir.path().join("a.rs")).unwrap(),
1790                    lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
1791                ))),
1792            )
1793            .await;
1794        let mut definitions = definitions.await.unwrap();
1795        assert_eq!(definitions.len(), 1);
1796        let definition = definitions.pop().unwrap();
1797        cx.update(|cx| {
1798            let target_buffer = definition.target_buffer.read(cx);
1799            assert_eq!(
1800                target_buffer.file().unwrap().abs_path(),
1801                Some(dir.path().join("a.rs"))
1802            );
1803            assert_eq!(definition.target_range.to_offset(target_buffer), 9..10);
1804            assert_eq!(
1805                list_worktrees(&project, cx),
1806                [
1807                    (dir.path().join("b.rs"), false),
1808                    (dir.path().join("a.rs"), true)
1809                ]
1810            );
1811
1812            drop(definition);
1813        });
1814        cx.read(|cx| {
1815            assert_eq!(
1816                list_worktrees(&project, cx),
1817                [(dir.path().join("b.rs"), false)]
1818            );
1819        });
1820
1821        fn list_worktrees(project: &ModelHandle<Project>, cx: &AppContext) -> Vec<(PathBuf, bool)> {
1822            project
1823                .read(cx)
1824                .worktrees(cx)
1825                .map(|worktree| {
1826                    let worktree = worktree.read(cx);
1827                    (
1828                        worktree.as_local().unwrap().abs_path().to_path_buf(),
1829                        worktree.is_weak(),
1830                    )
1831                })
1832                .collect::<Vec<_>>()
1833        }
1834    }
1835
1836    fn build_project(cx: &mut TestAppContext) -> ModelHandle<Project> {
1837        let languages = Arc::new(LanguageRegistry::new());
1838        let fs = Arc::new(RealFs);
1839        let http_client = FakeHttpClient::with_404_response();
1840        let client = client::Client::new(http_client.clone());
1841        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
1842        cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
1843    }
1844}