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 buffer_diff::BufferDiff;use client::Client;use collections::{hash_map, HashMap, HashSet};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 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::{self, ToProto}, AnyProtoClient, ErrorExt as _, TypedEnvelope,};use serde::Deserialize;use smol::channel::Receiver;use std::{ io, ops::Range, path::{Path, PathBuf}, pin::pin, 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); } pub fn wait_for_recalculation(&mut self) -> Option<oneshot::Receiver<()>> { if self.diff_updated_futures.is_empty() { return None; } let (tx, rx) = oneshot::channel(); self.diff_updated_futures.push(tx); Some(rx) } 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<()> { log::debug!("recalculate diffs"); 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 { let mut new_unstaged_diff = None; if let Some(unstaged_diff) = &unstaged_diff { new_unstaged_diff = Some( BufferDiff::update_diff( unstaged_diff.clone(), buffer.clone(), index, index_changed, language_changed, language.clone(), language_registry.clone(), &mut cx, ) .await?, ); } let mut new_uncommitted_diff = None; if let Some(uncommitted_diff) = &uncommitted_diff { new_uncommitted_diff = if index_matches_head { new_unstaged_diff.clone() } else { Some( BufferDiff::update_diff( uncommitted_diff.clone(), buffer.clone(), head, head_changed, language_changed, language.clone(), language_registry.clone(), &mut cx, ) .await?, ) } } let unstaged_changed_range = if let Some((unstaged_diff, new_unstaged_diff)) = unstaged_diff.as_ref().zip(new_unstaged_diff.clone()) { unstaged_diff.update(&mut cx, |diff, cx| { diff.set_snapshot(&buffer, new_unstaged_diff, language_changed, None, cx) })? } else { None }; if let Some((uncommitted_diff, new_uncommitted_diff)) = uncommitted_diff.as_ref().zip(new_uncommitted_diff.clone()) { uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| { uncommitted_diff.set_snapshot( &buffer, new_uncommitted_diff, language_changed, unstaged_changed_range, cx, ); })?; } if let Some(this) = this.upgrade() { this.update(&mut cx, |this, _| { this.index_changed = false; this.head_changed = false; this.language_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>), BufferDiffAdded(Entity<BufferDiff>), 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_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_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_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()Lines 1–500 of 2879. 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 buffer_diff::BufferDiff; use client::Client; use collections::{hash_map, HashMap, HashSet}; 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 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::{self, ToProto}, AnyProtoClient, ErrorExt as _, TypedEnvelope, }; use serde::Deserialize; use smol::channel::Receiver; use std::{ io, ops::Range, path::{Path, PathBuf}, pin::pin, 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); } pub fn wait_for_recalculation(&mut self) -> Option<oneshot::Receiver<()>> { if self.diff_updated_futures.is_empty() { return None; } let (tx, rx) = oneshot::channel(); self.diff_updated_futures.push(tx); Some(rx) } 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<()> { log::debug!("recalculate diffs"); 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 { let mut new_unstaged_diff = None; if let Some(unstaged_diff) = &unstaged_diff { new_unstaged_diff = Some( BufferDiff::update_diff( unstaged_diff.clone(), buffer.clone(), index, index_changed, language_changed, language.clone(), language_registry.clone(), &mut cx, ) .await?, ); } let mut new_uncommitted_diff = None; if let Some(uncommitted_diff) = &uncommitted_diff { new_uncommitted_diff = if index_matches_head { new_unstaged_diff.clone() } else { Some( BufferDiff::update_diff( uncommitted_diff.clone(), buffer.clone(), head, head_changed, language_changed, language.clone(), language_registry.clone(), &mut cx, ) .await?, ) } } let unstaged_changed_range = if let Some((unstaged_diff, new_unstaged_diff)) = unstaged_diff.as_ref().zip(new_unstaged_diff.clone()) { unstaged_diff.update(&mut cx, |diff, cx| { diff.set_snapshot(&buffer, new_unstaged_diff, language_changed, None, cx) })? } else { None }; if let Some((uncommitted_diff, new_uncommitted_diff)) = uncommitted_diff.as_ref().zip(new_uncommitted_diff.clone()) { uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| { uncommitted_diff.set_snapshot( &buffer, new_uncommitted_diff, language_changed, unstaged_changed_range, cx, ); })?; } if let Some(this) = this.upgrade() { this.update(&mut cx, |this, _| { this.index_changed = false; this.head_changed = false; this.language_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>), BufferDiffAdded(Entity<BufferDiff>), 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_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_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_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()