use crate::{ fs::Fs, language::LanguageRegistry, rpc::Client, util::TryFutureExt as _, worktree::{self, Worktree}, AppState, }; use anyhow::Result; use gpui::{Entity, ModelContext, ModelHandle, Task}; use std::{path::Path, sync::Arc}; pub struct Project { worktrees: Vec>, active_entry: Option<(usize, usize)>, languages: Arc, rpc: Arc, fs: Arc, } pub enum Event { ActiveEntryChanged(Option<(usize, usize)>), WorktreeRemoved(usize), } impl Project { pub fn new(app_state: &AppState) -> Self { Self { worktrees: Default::default(), active_entry: None, languages: app_state.languages.clone(), rpc: app_state.rpc.clone(), fs: app_state.fs.clone(), } } pub fn worktrees(&self) -> &[ModelHandle] { &self.worktrees } pub fn worktree_for_id(&self, id: usize) -> Option> { self.worktrees .iter() .find(|worktree| worktree.id() == id) .cloned() } pub fn add_local_worktree( &mut self, path: &Path, cx: &mut ModelContext, ) -> Task>> { let fs = self.fs.clone(); let rpc = self.rpc.clone(); let languages = self.languages.clone(); let path = Arc::from(path); cx.spawn(|this, mut cx| async move { let worktree = Worktree::open_local(rpc, path, fs, languages, &mut cx).await?; this.update(&mut cx, |this, cx| { this.add_worktree(worktree.clone(), cx); }); Ok(worktree) }) } pub fn add_remote_worktree( &mut self, remote_id: u64, cx: &mut ModelContext, ) -> Task>> { let rpc = self.rpc.clone(); let languages = self.languages.clone(); cx.spawn(|this, mut cx| async move { rpc.authenticate_and_connect(&cx).await?; let worktree = Worktree::open_remote(rpc.clone(), remote_id, languages, &mut cx).await?; this.update(&mut cx, |this, cx| { cx.subscribe(&worktree, move |this, _, event, cx| match event { worktree::Event::Closed => { this.close_remote_worktree(remote_id, cx); cx.notify(); } }) .detach(); this.add_worktree(worktree.clone(), cx); }); Ok(worktree) }) } fn add_worktree(&mut self, worktree: ModelHandle, cx: &mut ModelContext) { cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); self.worktrees.push(worktree); cx.notify(); } pub fn set_active_entry( &mut self, entry: Option<(usize, Arc)>, cx: &mut ModelContext, ) { let new_active_entry = entry.and_then(|(worktree_id, path)| { let worktree = self.worktree_for_id(worktree_id)?; let entry = worktree.read(cx).entry_for_path(path)?; Some((worktree_id, entry.id)) }); if new_active_entry != self.active_entry { self.active_entry = new_active_entry; cx.emit(Event::ActiveEntryChanged(new_active_entry)); } } pub fn active_entry(&self) -> Option<(usize, usize)> { self.active_entry } pub fn share_worktree(&self, remote_id: u64, cx: &mut ModelContext) { let rpc = self.rpc.clone(); cx.spawn(|this, mut cx| { async move { rpc.authenticate_and_connect(&cx).await?; let task = this.update(&mut cx, |this, cx| { for worktree in &this.worktrees { let task = worktree.update(cx, |worktree, cx| { worktree.as_local_mut().and_then(|worktree| { if worktree.remote_id() == Some(remote_id) { Some(worktree.share(cx)) } else { None } }) }); if task.is_some() { return task; } } None }); if let Some(task) = task { task.await?; } Ok(()) } .log_err() }) .detach(); } pub fn unshare_worktree(&mut self, remote_id: u64, cx: &mut ModelContext) { for worktree in &self.worktrees { if worktree.update(cx, |worktree, cx| { if let Some(worktree) = worktree.as_local_mut() { if worktree.remote_id() == Some(remote_id) { worktree.unshare(cx); return true; } } false }) { break; } } } pub fn close_remote_worktree(&mut self, id: u64, cx: &mut ModelContext) { self.worktrees.retain(|worktree| { let keep = worktree.update(cx, |worktree, cx| { if let Some(worktree) = worktree.as_remote_mut() { if worktree.remote_id() == id { worktree.close_all_buffers(cx); return false; } } true }); if !keep { cx.emit(Event::WorktreeRemoved(worktree.id())); } keep }); } } impl Entity for Project { type Event = Event; }