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