use crate::{ lsp_store::OpenLspBufferHandle, search::SearchQuery, worktree_store::{WorktreeStore, WorktreeStoreEvent}, ProjectItem as _, ProjectPath,};use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};use anyhow::{anyhow, bail, Context as _, Result};use client::Client;use collections::{hash_map, HashMap, HashSet};use diff::{BufferDiff, BufferDiffEvent, BufferDiffSnapshot};use fs::Fs;use futures::{channel::oneshot, future::Shared, Future, FutureExt as _, StreamExt};use git::{blame::Blame, repository::RepoPath};use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,};use http_client::Url;use language::{ proto::{ deserialize_line_ending, deserialize_version, serialize_line_ending, serialize_version, split_operations, }, Buffer, BufferEvent, Capability, DiskState, File as _, Language, LanguageRegistry, Operation,};use rpc::{proto, AnyProtoClient, ErrorExt as _, TypedEnvelope};use serde::Deserialize;use smol::channel::Receiver;use std::{ io, ops::Range, path::{Path, PathBuf}, pin::pin, str::FromStr as _, sync::Arc, time::Instant,};use text::BufferId;use util::{debug_panic, maybe, ResultExt as _, TryFutureExt};use worktree::{File, PathChange, ProjectEntryId, UpdatedGitRepositoriesSet, Worktree, WorktreeId}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]enum DiffKind { Unstaged, Uncommitted,} /// A set of open buffers.pub struct BufferStore { state: BufferStoreState, #[allow(clippy::type_complexity)] loading_buffers: HashMap<ProjectPath, Shared<Task<Result<Entity<Buffer>, Arc<anyhow::Error>>>>>, #[allow(clippy::type_complexity)] loading_diffs: HashMap<(BufferId, DiffKind), Shared<Task<Result<Entity<BufferDiff>, Arc<anyhow::Error>>>>>, worktree_store: Entity<WorktreeStore>, opened_buffers: HashMap<BufferId, OpenBuffer>, downstream_client: Option<(AnyProtoClient, u64)>, shared_buffers: HashMap<proto::PeerId, HashMap<BufferId, SharedBuffer>>,} #[derive(Hash, Eq, PartialEq, Clone)]struct SharedBuffer { buffer: Entity<Buffer>, diff: Option<Entity<BufferDiff>>, lsp_handle: Option<OpenLspBufferHandle>,} #[derive(Default)]struct BufferDiffState { unstaged_diff: Option<WeakEntity<BufferDiff>>, uncommitted_diff: Option<WeakEntity<BufferDiff>>, recalculate_diff_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, language_registry: Option<Arc<LanguageRegistry>>, diff_updated_futures: Vec<oneshot::Sender<()>>, head_text: Option<Arc<String>>, index_text: Option<Arc<String>>, head_changed: bool, index_changed: bool, language_changed: bool,} #[derive(Clone, Debug)]enum DiffBasesChange { SetIndex(Option<String>), SetHead(Option<String>), SetEach { index: Option<String>, head: Option<String>, }, SetBoth(Option<String>),} impl BufferDiffState { fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) { self.language = buffer.read(cx).language().cloned(); self.language_changed = true; let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx); } fn unstaged_diff(&self) -> Option<Entity<BufferDiff>> { self.unstaged_diff.as_ref().and_then(|set| set.upgrade()) } fn uncommitted_diff(&self) -> Option<Entity<BufferDiff>> { self.uncommitted_diff.as_ref().and_then(|set| set.upgrade()) } fn handle_base_texts_updated( &mut self, buffer: text::BufferSnapshot, message: proto::UpdateDiffBases, cx: &mut Context<Self>, ) { use proto::update_diff_bases::Mode; let Some(mode) = Mode::from_i32(message.mode) else { return; }; let diff_bases_change = match mode { Mode::HeadOnly => DiffBasesChange::SetHead(message.committed_text), Mode::IndexOnly => DiffBasesChange::SetIndex(message.staged_text), Mode::IndexMatchesHead => DiffBasesChange::SetBoth(message.committed_text), Mode::IndexAndHead => DiffBasesChange::SetEach { index: message.staged_text, head: message.committed_text, }, }; let _ = self.diff_bases_changed(buffer, diff_bases_change, cx); } fn diff_bases_changed( &mut self, buffer: text::BufferSnapshot, diff_bases_change: DiffBasesChange, cx: &mut Context<Self>, ) -> oneshot::Receiver<()> { match diff_bases_change { DiffBasesChange::SetIndex(index) => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); Arc::new(index) }); self.index_changed = true; } DiffBasesChange::SetHead(head) => { self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); Arc::new(head) }); self.head_changed = true; } DiffBasesChange::SetBoth(text) => { let text = text.map(|mut text| { text::LineEnding::normalize(&mut text); Arc::new(text) }); self.head_text = text.clone(); self.index_text = text; self.head_changed = true; self.index_changed = true; } DiffBasesChange::SetEach { index, head } => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); Arc::new(index) }); self.index_changed = true; self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); Arc::new(head) }); self.head_changed = true; } } self.recalculate_diffs(buffer, cx) } fn recalculate_diffs( &mut self, buffer: text::BufferSnapshot, cx: &mut Context<Self>, ) -> oneshot::Receiver<()> { let (tx, rx) = oneshot::channel(); self.diff_updated_futures.push(tx); let language = self.language.clone(); let language_registry = self.language_registry.clone(); let unstaged_diff = self.unstaged_diff(); let uncommitted_diff = self.uncommitted_diff(); let head = self.head_text.clone(); let index = self.index_text.clone(); let index_changed = self.index_changed; let head_changed = self.head_changed; let language_changed = self.language_changed; let index_matches_head = match (self.index_text.as_ref(), self.head_text.as_ref()) { (Some(index), Some(head)) => Arc::ptr_eq(index, head), (None, None) => true, _ => false, }; self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move { if let Some(unstaged_diff) = &unstaged_diff { let snapshot = if index_changed || language_changed { cx.update(|cx| { BufferDiffSnapshot::build( buffer.clone(), index, language.clone(), language_registry.clone(), cx, ) })? .await } else { unstaged_diff .read_with(&cx, |changes, cx| { BufferDiffSnapshot::build_with_base_buffer( buffer.clone(), index, changes.snapshot.base_text.clone(), cx, ) })? .await }; unstaged_diff.update(&mut cx, |unstaged_diff, cx| { unstaged_diff.set_state(snapshot, &buffer, cx); if language_changed { cx.emit(BufferDiffEvent::LanguageChanged); } })?; } if let Some(uncommitted_diff) = &uncommitted_diff { let snapshot = if let (Some(unstaged_diff), true) = (&unstaged_diff, index_matches_head) { unstaged_diff.read_with(&cx, |diff, _| diff.snapshot.clone())? } else if head_changed || language_changed { cx.update(|cx| { BufferDiffSnapshot::build( buffer.clone(), head, language.clone(), language_registry.clone(), cx, ) })? .await } else { uncommitted_diff .read_with(&cx, |changes, cx| { BufferDiffSnapshot::build_with_base_buffer( buffer.clone(), head, changes.snapshot.base_text.clone(), cx, ) })? .await }; uncommitted_diff.update(&mut cx, |diff, cx| { diff.set_state(snapshot, &buffer, cx); if language_changed { cx.emit(BufferDiffEvent::LanguageChanged); } })?; } if let Some(this) = this.upgrade() { this.update(&mut cx, |this, _| { this.index_changed = false; this.head_changed = false; for tx in this.diff_updated_futures.drain(..) { tx.send(()).ok(); } })?; } Ok(()) })); rx }} enum BufferStoreState { Local(LocalBufferStore), Remote(RemoteBufferStore),} struct RemoteBufferStore { shared_with_me: HashSet<Entity<Buffer>>, upstream_client: AnyProtoClient, project_id: u64, loading_remote_buffers_by_id: HashMap<BufferId, Entity<Buffer>>, remote_buffer_listeners: HashMap<BufferId, Vec<oneshot::Sender<Result<Entity<Buffer>, anyhow::Error>>>>, worktree_store: Entity<WorktreeStore>,} struct LocalBufferStore { local_buffer_ids_by_path: HashMap<ProjectPath, BufferId>, local_buffer_ids_by_entry_id: HashMap<ProjectEntryId, BufferId>, worktree_store: Entity<WorktreeStore>, _subscription: Subscription,} enum OpenBuffer { Complete { buffer: WeakEntity<Buffer>, diff_state: Entity<BufferDiffState>, }, Operations(Vec<Operation>),} pub enum BufferStoreEvent { BufferAdded(Entity<Buffer>), BufferDropped(BufferId), BufferChangedFilePath { buffer: Entity<Buffer>, old_file: Option<Arc<dyn language::File>>, },} #[derive(Default, Debug)]pub struct ProjectTransaction(pub HashMap<Entity<Buffer>, language::Transaction>); impl EventEmitter<BufferStoreEvent> for BufferStore {} impl RemoteBufferStore { fn open_unstaged_diff(&self, buffer_id: BufferId, cx: &App) -> Task<Result<Option<String>>> { let project_id = self.project_id; let client = self.upstream_client.clone(); cx.background_executor().spawn(async move { let response = client .request(proto::OpenUnstagedDiff { project_id, buffer_id: buffer_id.to_proto(), }) .await?; Ok(response.staged_text) }) } fn open_uncommitted_diff( &self, buffer_id: BufferId, cx: &App, ) -> Task<Result<DiffBasesChange>> { use proto::open_uncommitted_diff_response::Mode; let project_id = self.project_id; let client = self.upstream_client.clone(); cx.background_executor().spawn(async move { let response = client .request(proto::OpenUncommittedDiff { project_id, buffer_id: buffer_id.to_proto(), }) .await?; let mode = Mode::from_i32(response.mode).ok_or_else(|| anyhow!("Invalid mode"))?; let bases = match mode { Mode::IndexMatchesHead => DiffBasesChange::SetBoth(response.committed_text), Mode::IndexAndHead => DiffBasesChange::SetEach { head: response.committed_text, index: response.staged_text, }, }; Ok(bases) }) } pub fn wait_for_remote_buffer( &mut self, id: BufferId, cx: &mut Context<BufferStore>, ) -> Task<Result<Entity<Buffer>>> { let (tx, rx) = oneshot::channel(); self.remote_buffer_listeners.entry(id).or_default().push(tx); cx.spawn(|this, cx| async move { if let Some(buffer) = this .read_with(&cx, |buffer_store, _| buffer_store.get(id)) .ok() .flatten() { return Ok(buffer); } cx.background_executor() .spawn(async move { rx.await? }) .await }) } fn save_remote_buffer( &self, buffer_handle: Entity<Buffer>, new_path: Option<proto::ProjectPath>, cx: &Context<BufferStore>, ) -> Task<Result<()>> { let buffer = buffer_handle.read(cx); let buffer_id = buffer.remote_id().into(); let version = buffer.version(); let rpc = self.upstream_client.clone(); let project_id = self.project_id; cx.spawn(move |_, mut cx| async move { let response = rpc .request(proto::SaveBuffer { project_id, buffer_id, new_path, version: serialize_version(&version), }) .await?; let version = deserialize_version(&response.version); let mtime = response.mtime.map(|mtime| mtime.into()); buffer_handle.update(&mut cx, |buffer, cx| { buffer.did_save(version.clone(), mtime, cx); })?; Ok(()) }) } pub fn handle_create_buffer_for_peer( &mut self, envelope: TypedEnvelope<proto::CreateBufferForPeer>, replica_id: u16, capability: Capability, cx: &mut Context<BufferStore>, ) -> Result<Option<Entity<Buffer>>> { match envelope .payload .variant .ok_or_else(|| anyhow!("missing variant"))? { proto::create_buffer_for_peer::Variant::State(mut state) => { let buffer_id = BufferId::new(state.id)?; let buffer_result = maybe!({ let mut buffer_file = None; if let Some(file) = state.file.take() { let worktree_id = worktree::WorktreeId::from_proto(file.worktree_id); let worktree = self .worktree_store .read(cx) .worktree_for_id(worktree_id, cx) .ok_or_else(|| { anyhow!("no worktree found for id {}", file.worktree_id) })?; buffer_file = Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?) as Arc<dyn language::File>); } Buffer::from_proto(replica_id, capability, state, buffer_file) }); match buffer_result { Ok(buffer) => { let buffer = cx.new(|_| buffer); self.loading_remote_buffers_by_id.insert(buffer_id, buffer); } Err(error) => { if let Some(listeners) = self.remote_buffer_listeners.remove(&buffer_id) { for listener in listeners { listener.send(Err(anyhow!(error.cloned()))).ok(); } } } } } proto::create_buffer_for_peer::Variant::Chunk(chunk) => { let buffer_id = BufferId::new(chunk.buffer_id)?; let buffer = self .loading_remote_buffers_by_id .get(&buffer_id) .cloned() .ok_or_else(|| { anyhow!( "received chunk for buffer {} without initial state", chunk.buffer_id ) })?; let result = maybe!({ let operations = chunk .operations .into_iter() .map(language::proto::deserialize_operation) .collect::<Result<Vec<_>>>()?; buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx)); anyhow::Ok(())Lines 1–500 of 2867. Load more
use crate::{ lsp_store::OpenLspBufferHandle, search::SearchQuery, worktree_store::{WorktreeStore, WorktreeStoreEvent}, ProjectItem as _, ProjectPath, }; use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry}; use anyhow::{anyhow, bail, Context as _, Result}; use client::Client; use collections::{hash_map, HashMap, HashSet}; use diff::{BufferDiff, BufferDiffEvent, BufferDiffSnapshot}; use fs::Fs; use futures::{channel::oneshot, future::Shared, Future, FutureExt as _, StreamExt}; use git::{blame::Blame, repository::RepoPath}; use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity, }; use http_client::Url; use language::{ proto::{ deserialize_line_ending, deserialize_version, serialize_line_ending, serialize_version, split_operations, }, Buffer, BufferEvent, Capability, DiskState, File as _, Language, LanguageRegistry, Operation, }; use rpc::{proto, AnyProtoClient, ErrorExt as _, TypedEnvelope}; use serde::Deserialize; use smol::channel::Receiver; use std::{ io, ops::Range, path::{Path, PathBuf}, pin::pin, str::FromStr as _, sync::Arc, time::Instant, }; use text::BufferId; use util::{debug_panic, maybe, ResultExt as _, TryFutureExt}; use worktree::{File, PathChange, ProjectEntryId, UpdatedGitRepositoriesSet, Worktree, WorktreeId}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] enum DiffKind { Unstaged, Uncommitted, } /// A set of open buffers. pub struct BufferStore { state: BufferStoreState, #[allow(clippy::type_complexity)] loading_buffers: HashMap<ProjectPath, Shared<Task<Result<Entity<Buffer>, Arc<anyhow::Error>>>>>, #[allow(clippy::type_complexity)] loading_diffs: HashMap<(BufferId, DiffKind), Shared<Task<Result<Entity<BufferDiff>, Arc<anyhow::Error>>>>>, worktree_store: Entity<WorktreeStore>, opened_buffers: HashMap<BufferId, OpenBuffer>, downstream_client: Option<(AnyProtoClient, u64)>, shared_buffers: HashMap<proto::PeerId, HashMap<BufferId, SharedBuffer>>, } #[derive(Hash, Eq, PartialEq, Clone)] struct SharedBuffer { buffer: Entity<Buffer>, diff: Option<Entity<BufferDiff>>, lsp_handle: Option<OpenLspBufferHandle>, } #[derive(Default)] struct BufferDiffState { unstaged_diff: Option<WeakEntity<BufferDiff>>, uncommitted_diff: Option<WeakEntity<BufferDiff>>, recalculate_diff_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, language_registry: Option<Arc<LanguageRegistry>>, diff_updated_futures: Vec<oneshot::Sender<()>>, head_text: Option<Arc<String>>, index_text: Option<Arc<String>>, head_changed: bool, index_changed: bool, language_changed: bool, } #[derive(Clone, Debug)] enum DiffBasesChange { SetIndex(Option<String>), SetHead(Option<String>), SetEach { index: Option<String>, head: Option<String>, }, SetBoth(Option<String>), } impl BufferDiffState { fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) { self.language = buffer.read(cx).language().cloned(); self.language_changed = true; let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx); } fn unstaged_diff(&self) -> Option<Entity<BufferDiff>> { self.unstaged_diff.as_ref().and_then(|set| set.upgrade()) } fn uncommitted_diff(&self) -> Option<Entity<BufferDiff>> { self.uncommitted_diff.as_ref().and_then(|set| set.upgrade()) } fn handle_base_texts_updated( &mut self, buffer: text::BufferSnapshot, message: proto::UpdateDiffBases, cx: &mut Context<Self>, ) { use proto::update_diff_bases::Mode; let Some(mode) = Mode::from_i32(message.mode) else { return; }; let diff_bases_change = match mode { Mode::HeadOnly => DiffBasesChange::SetHead(message.committed_text), Mode::IndexOnly => DiffBasesChange::SetIndex(message.staged_text), Mode::IndexMatchesHead => DiffBasesChange::SetBoth(message.committed_text), Mode::IndexAndHead => DiffBasesChange::SetEach { index: message.staged_text, head: message.committed_text, }, }; let _ = self.diff_bases_changed(buffer, diff_bases_change, cx); } fn diff_bases_changed( &mut self, buffer: text::BufferSnapshot, diff_bases_change: DiffBasesChange, cx: &mut Context<Self>, ) -> oneshot::Receiver<()> { match diff_bases_change { DiffBasesChange::SetIndex(index) => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); Arc::new(index) }); self.index_changed = true; } DiffBasesChange::SetHead(head) => { self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); Arc::new(head) }); self.head_changed = true; } DiffBasesChange::SetBoth(text) => { let text = text.map(|mut text| { text::LineEnding::normalize(&mut text); Arc::new(text) }); self.head_text = text.clone(); self.index_text = text; self.head_changed = true; self.index_changed = true; } DiffBasesChange::SetEach { index, head } => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); Arc::new(index) }); self.index_changed = true; self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); Arc::new(head) }); self.head_changed = true; } } self.recalculate_diffs(buffer, cx) } fn recalculate_diffs( &mut self, buffer: text::BufferSnapshot, cx: &mut Context<Self>, ) -> oneshot::Receiver<()> { let (tx, rx) = oneshot::channel(); self.diff_updated_futures.push(tx); let language = self.language.clone(); let language_registry = self.language_registry.clone(); let unstaged_diff = self.unstaged_diff(); let uncommitted_diff = self.uncommitted_diff(); let head = self.head_text.clone(); let index = self.index_text.clone(); let index_changed = self.index_changed; let head_changed = self.head_changed; let language_changed = self.language_changed; let index_matches_head = match (self.index_text.as_ref(), self.head_text.as_ref()) { (Some(index), Some(head)) => Arc::ptr_eq(index, head), (None, None) => true, _ => false, }; self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move { if let Some(unstaged_diff) = &unstaged_diff { let snapshot = if index_changed || language_changed { cx.update(|cx| { BufferDiffSnapshot::build( buffer.clone(), index, language.clone(), language_registry.clone(), cx, ) })? .await } else { unstaged_diff .read_with(&cx, |changes, cx| { BufferDiffSnapshot::build_with_base_buffer( buffer.clone(), index, changes.snapshot.base_text.clone(), cx, ) })? .await }; unstaged_diff.update(&mut cx, |unstaged_diff, cx| { unstaged_diff.set_state(snapshot, &buffer, cx); if language_changed { cx.emit(BufferDiffEvent::LanguageChanged); } })?; } if let Some(uncommitted_diff) = &uncommitted_diff { let snapshot = if let (Some(unstaged_diff), true) = (&unstaged_diff, index_matches_head) { unstaged_diff.read_with(&cx, |diff, _| diff.snapshot.clone())? } else if head_changed || language_changed { cx.update(|cx| { BufferDiffSnapshot::build( buffer.clone(), head, language.clone(), language_registry.clone(), cx, ) })? .await } else { uncommitted_diff .read_with(&cx, |changes, cx| { BufferDiffSnapshot::build_with_base_buffer( buffer.clone(), head, changes.snapshot.base_text.clone(), cx, ) })? .await }; uncommitted_diff.update(&mut cx, |diff, cx| { diff.set_state(snapshot, &buffer, cx); if language_changed { cx.emit(BufferDiffEvent::LanguageChanged); } })?; } if let Some(this) = this.upgrade() { this.update(&mut cx, |this, _| { this.index_changed = false; this.head_changed = false; for tx in this.diff_updated_futures.drain(..) { tx.send(()).ok(); } })?; } Ok(()) })); rx } } enum BufferStoreState { Local(LocalBufferStore), Remote(RemoteBufferStore), } struct RemoteBufferStore { shared_with_me: HashSet<Entity<Buffer>>, upstream_client: AnyProtoClient, project_id: u64, loading_remote_buffers_by_id: HashMap<BufferId, Entity<Buffer>>, remote_buffer_listeners: HashMap<BufferId, Vec<oneshot::Sender<Result<Entity<Buffer>, anyhow::Error>>>>, worktree_store: Entity<WorktreeStore>, } struct LocalBufferStore { local_buffer_ids_by_path: HashMap<ProjectPath, BufferId>, local_buffer_ids_by_entry_id: HashMap<ProjectEntryId, BufferId>, worktree_store: Entity<WorktreeStore>, _subscription: Subscription, } enum OpenBuffer { Complete { buffer: WeakEntity<Buffer>, diff_state: Entity<BufferDiffState>, }, Operations(Vec<Operation>), } pub enum BufferStoreEvent { BufferAdded(Entity<Buffer>), BufferDropped(BufferId), BufferChangedFilePath { buffer: Entity<Buffer>, old_file: Option<Arc<dyn language::File>>, }, } #[derive(Default, Debug)] pub struct ProjectTransaction(pub HashMap<Entity<Buffer>, language::Transaction>); impl EventEmitter<BufferStoreEvent> for BufferStore {} impl RemoteBufferStore { fn open_unstaged_diff(&self, buffer_id: BufferId, cx: &App) -> Task<Result<Option<String>>> { let project_id = self.project_id; let client = self.upstream_client.clone(); cx.background_executor().spawn(async move { let response = client .request(proto::OpenUnstagedDiff { project_id, buffer_id: buffer_id.to_proto(), }) .await?; Ok(response.staged_text) }) } fn open_uncommitted_diff( &self, buffer_id: BufferId, cx: &App, ) -> Task<Result<DiffBasesChange>> { use proto::open_uncommitted_diff_response::Mode; let project_id = self.project_id; let client = self.upstream_client.clone(); cx.background_executor().spawn(async move { let response = client .request(proto::OpenUncommittedDiff { project_id, buffer_id: buffer_id.to_proto(), }) .await?; let mode = Mode::from_i32(response.mode).ok_or_else(|| anyhow!("Invalid mode"))?; let bases = match mode { Mode::IndexMatchesHead => DiffBasesChange::SetBoth(response.committed_text), Mode::IndexAndHead => DiffBasesChange::SetEach { head: response.committed_text, index: response.staged_text, }, }; Ok(bases) }) } pub fn wait_for_remote_buffer( &mut self, id: BufferId, cx: &mut Context<BufferStore>, ) -> Task<Result<Entity<Buffer>>> { let (tx, rx) = oneshot::channel(); self.remote_buffer_listeners.entry(id).or_default().push(tx); cx.spawn(|this, cx| async move { if let Some(buffer) = this .read_with(&cx, |buffer_store, _| buffer_store.get(id)) .ok() .flatten() { return Ok(buffer); } cx.background_executor() .spawn(async move { rx.await? }) .await }) } fn save_remote_buffer( &self, buffer_handle: Entity<Buffer>, new_path: Option<proto::ProjectPath>, cx: &Context<BufferStore>, ) -> Task<Result<()>> { let buffer = buffer_handle.read(cx); let buffer_id = buffer.remote_id().into(); let version = buffer.version(); let rpc = self.upstream_client.clone(); let project_id = self.project_id; cx.spawn(move |_, mut cx| async move { let response = rpc .request(proto::SaveBuffer { project_id, buffer_id, new_path, version: serialize_version(&version), }) .await?; let version = deserialize_version(&response.version); let mtime = response.mtime.map(|mtime| mtime.into()); buffer_handle.update(&mut cx, |buffer, cx| { buffer.did_save(version.clone(), mtime, cx); })?; Ok(()) }) } pub fn handle_create_buffer_for_peer( &mut self, envelope: TypedEnvelope<proto::CreateBufferForPeer>, replica_id: u16, capability: Capability, cx: &mut Context<BufferStore>, ) -> Result<Option<Entity<Buffer>>> { match envelope .payload .variant .ok_or_else(|| anyhow!("missing variant"))? { proto::create_buffer_for_peer::Variant::State(mut state) => { let buffer_id = BufferId::new(state.id)?; let buffer_result = maybe!({ let mut buffer_file = None; if let Some(file) = state.file.take() { let worktree_id = worktree::WorktreeId::from_proto(file.worktree_id); let worktree = self .worktree_store .read(cx) .worktree_for_id(worktree_id, cx) .ok_or_else(|| { anyhow!("no worktree found for id {}", file.worktree_id) })?; buffer_file = Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?) as Arc<dyn language::File>); } Buffer::from_proto(replica_id, capability, state, buffer_file) }); match buffer_result { Ok(buffer) => { let buffer = cx.new(|_| buffer); self.loading_remote_buffers_by_id.insert(buffer_id, buffer); } Err(error) => { if let Some(listeners) = self.remote_buffer_listeners.remove(&buffer_id) { for listener in listeners { listener.send(Err(anyhow!(error.cloned()))).ok(); } } } } } proto::create_buffer_for_peer::Variant::Chunk(chunk) => { let buffer_id = BufferId::new(chunk.buffer_id)?; let buffer = self .loading_remote_buffers_by_id .get(&buffer_id) .cloned() .ok_or_else(|| { anyhow!( "received chunk for buffer {} without initial state", chunk.buffer_id ) })?; let result = maybe!({ let operations = chunk .operations .into_iter() .map(language::proto::deserialize_operation) .collect::<Result<Vec<_>>>()?; buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx)); anyhow::Ok(())