This commit is contained in:
Antonio Scandurra 2023-10-24 10:31:35 +02:00
parent 18eb4a7292
commit 953857f8e3
5 changed files with 69 additions and 54 deletions

2
Cargo.lock generated
View file

@ -6005,7 +6005,7 @@ dependencies = [
"node_runtime", "node_runtime",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",
"prettier", "prettier2",
"pretty_assertions", "pretty_assertions",
"rand 0.8.5", "rand 0.8.5",
"regex", "regex",

View file

@ -316,6 +316,9 @@ impl AppContext {
Effect::NotifyGlobalObservers { global_type } => { Effect::NotifyGlobalObservers { global_type } => {
self.apply_notify_global_observers_effect(global_type); self.apply_notify_global_observers_effect(global_type);
} }
Effect::Defer { callback } => {
self.apply_defer_effect(callback);
}
} }
} else { } else {
break; break;
@ -436,6 +439,10 @@ impl AppContext {
.retain(&type_id, |observer| observer(self)); .retain(&type_id, |observer| observer(self));
} }
fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + Send + Sync + 'static>) {
callback(self);
}
pub fn to_async(&self) -> AsyncAppContext { pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext { AsyncAppContext {
app: unsafe { mem::transmute(self.this.clone()) }, app: unsafe { mem::transmute(self.this.clone()) },
@ -496,6 +503,12 @@ impl AppContext {
}) })
} }
pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static + Send + Sync) {
self.push_effect(Effect::Defer {
callback: Box::new(f),
});
}
pub fn text_system(&self) -> &Arc<TextSystem> { pub fn text_system(&self) -> &Arc<TextSystem> {
&self.text_system &self.text_system
} }
@ -772,6 +785,9 @@ pub(crate) enum Effect {
NotifyGlobalObservers { NotifyGlobalObservers {
global_type: TypeId, global_type: TypeId,
}, },
Defer {
callback: Box<dyn FnOnce(&mut AppContext) + Send + Sync + 'static>,
},
} }
pub(crate) struct AnyDrag { pub(crate) struct AnyDrag {

View file

@ -15,7 +15,7 @@ test-support = [
"language2/test-support", "language2/test-support",
"settings2/test-support", "settings2/test-support",
"text/test-support", "text/test-support",
"prettier/test-support", "prettier2/test-support",
] ]
[dependencies] [dependencies]
@ -33,7 +33,7 @@ gpui2 = { path = "../gpui2" }
language2 = { path = "../language2" } language2 = { path = "../language2" }
lsp2 = { path = "../lsp2" } lsp2 = { path = "../lsp2" }
node_runtime = { path = "../node_runtime" } node_runtime = { path = "../node_runtime" }
prettier = { path = "../prettier" } prettier2 = { path = "../prettier2" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
settings2 = { path = "../settings2" } settings2 = { path = "../settings2" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }
@ -76,7 +76,7 @@ gpui2 = { path = "../gpui2", features = ["test-support"] }
language2 = { path = "../language2", features = ["test-support"] } language2 = { path = "../language2", features = ["test-support"] }
lsp2 = { path = "../lsp2", features = ["test-support"] } lsp2 = { path = "../lsp2", features = ["test-support"] }
settings2 = { path = "../settings2", features = ["test-support"] } settings2 = { path = "../settings2", features = ["test-support"] }
prettier = { path = "../prettier", features = ["test-support"] } prettier2 = { path = "../prettier2", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] } util = { path = "../util", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] }
git2.workspace = true git2.workspace = true

View file

@ -26,8 +26,8 @@ use futures::{
}; };
use globset::{Glob, GlobSet, GlobSetBuilder}; use globset::{Glob, GlobSet, GlobSetBuilder};
use gpui2::{ use gpui2::{
AnyHandle, AppContext, AsyncAppContext, EventEmitter, Executor, Handle, ModelContext, Task, AnyHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Handle, ModelContext,
WeakHandle, Task, WeakHandle,
}; };
use itertools::Itertools; use itertools::Itertools;
use language2::{ use language2::{
@ -53,7 +53,7 @@ use lsp2::{
use lsp_command::*; use lsp_command::*;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use postage::watch; use postage::watch;
use prettier::{LocateStart, Prettier, PRETTIER_SERVER_FILE, PRETTIER_SERVER_JS}; use prettier2::{LocateStart, Prettier, PRETTIER_SERVER_FILE, PRETTIER_SERVER_JS};
use project_settings::{LspSettings, ProjectSettings}; use project_settings::{LspSettings, ProjectSettings};
use rand::prelude::*; use rand::prelude::*;
use search::SearchQuery; use search::SearchQuery;
@ -186,7 +186,7 @@ impl DelayedDebounced {
fn fire_new<F>(&mut self, delay: Duration, cx: &mut ModelContext<Project>, func: F) fn fire_new<F>(&mut self, delay: Duration, cx: &mut ModelContext<Project>, func: F)
where where
F: 'static + FnOnce(&mut Project, &mut ModelContext<Project>) -> Task<()>, F: 'static + Send + FnOnce(&mut Project, &mut ModelContext<Project>) -> Task<()>,
{ {
if let Some(channel) = self.cancel_channel.take() { if let Some(channel) = self.cancel_channel.take() {
_ = channel.send(()); _ = channel.send(());
@ -256,7 +256,7 @@ enum ProjectClientState {
Local { Local {
remote_id: u64, remote_id: u64,
updates_tx: mpsc::UnboundedSender<LocalProjectUpdate>, updates_tx: mpsc::UnboundedSender<LocalProjectUpdate>,
_send_updates: Task<()>, _send_updates: Task<Result<()>>,
}, },
Remote { Remote {
sharing_has_stopped: bool, sharing_has_stopped: bool,
@ -702,14 +702,13 @@ impl Project {
let mut worktrees = Vec::new(); let mut worktrees = Vec::new();
for worktree in response.payload.worktrees { for worktree in response.payload.worktrees {
let worktree = cx.update(|cx| { let worktree =
Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx) Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx);
});
worktrees.push(worktree); worktrees.push(worktree);
} }
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx)) cx.spawn(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
.detach(); .detach();
let copilot_lsp_subscription = let copilot_lsp_subscription =
Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx)); Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx));
@ -1402,26 +1401,25 @@ impl Project {
updates_tx, updates_tx,
_send_updates: cx.spawn(move |this, mut cx| async move { _send_updates: cx.spawn(move |this, mut cx| async move {
while let Some(update) = updates_rx.next().await { while let Some(update) = updates_rx.next().await {
let Some(this) = this.upgrade() else { break };
match update { match update {
LocalProjectUpdate::WorktreesChanged => { LocalProjectUpdate::WorktreesChanged => {
let worktrees = let worktrees = this.update(&mut cx, |this, cx| {
this.read(&cx, |this, cx| this.worktrees(cx).collect::<Vec<_>>()); this.worktrees(cx).collect::<Vec<_>>()
})?;
let update_project = this let update_project = this
.read(&cx, |this, cx| { .update(&mut cx, |this, cx| {
this.client.request(proto::UpdateProject { this.client.request(proto::UpdateProject {
project_id, project_id,
worktrees: this.worktree_metadata_protos(cx), worktrees: this.worktree_metadata_protos(cx),
}) })
}) })?
.await; .await;
if update_project.is_ok() { if update_project.is_ok() {
for worktree in worktrees { for worktree in worktrees {
worktree.update(&mut cx, |worktree, cx| { worktree.update(&mut cx, |worktree, cx| {
let worktree = worktree.as_local_mut().unwrap(); let worktree = worktree.as_local_mut().unwrap();
worktree.share(project_id, cx).detach_and_log_err(cx) worktree.share(project_id, cx).detach_and_log_err(cx)
}); })?;
} }
} }
} }
@ -1439,7 +1437,7 @@ impl Project {
} else { } else {
None None
} }
}); })?;
let Some(buffer) = buffer else { continue }; let Some(buffer) = buffer else { continue };
let operations = let operations =
@ -1481,6 +1479,7 @@ impl Project {
} }
} }
} }
Ok(())
}), }),
}); });
@ -2169,26 +2168,26 @@ impl Project {
this: WeakHandle<Self>, this: WeakHandle<Self>,
rx: UnboundedReceiver<BufferOrderedMessage>, rx: UnboundedReceiver<BufferOrderedMessage>,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Option<()> { ) -> Result<()> {
const MAX_BATCH_SIZE: usize = 128; const MAX_BATCH_SIZE: usize = 128;
let mut operations_by_buffer_id = HashMap::default(); let mut operations_by_buffer_id = HashMap::default();
async fn flush_operations( async fn flush_operations(
this: &Handle<Project>, this: &WeakHandle<Project>,
operations_by_buffer_id: &mut HashMap<u64, Vec<proto::Operation>>, operations_by_buffer_id: &mut HashMap<u64, Vec<proto::Operation>>,
needs_resync_with_host: &mut bool, needs_resync_with_host: &mut bool,
is_local: bool, is_local: bool,
cx: &AsyncAppContext, cx: &mut AsyncAppContext,
) { ) -> Result<()> {
for (buffer_id, operations) in operations_by_buffer_id.drain() { for (buffer_id, operations) in operations_by_buffer_id.drain() {
let request = this.read(cx, |this, _| { let request = this.update(cx, |this, _| {
let project_id = this.remote_id()?; let project_id = this.remote_id()?;
Some(this.client.request(proto::UpdateBuffer { Some(this.client.request(proto::UpdateBuffer {
buffer_id, buffer_id,
project_id, project_id,
operations, operations,
})) }))
}); })?;
if let Some(request) = request { if let Some(request) = request {
if request.await.is_err() && !is_local { if request.await.is_err() && !is_local {
*needs_resync_with_host = true; *needs_resync_with_host = true;
@ -2196,14 +2195,14 @@ impl Project {
} }
} }
} }
Ok(())
} }
let mut needs_resync_with_host = false; let mut needs_resync_with_host = false;
let mut changes = rx.ready_chunks(MAX_BATCH_SIZE); let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
while let Some(changes) = changes.next().await { while let Some(changes) = changes.next().await {
let this = this.upgrade()?; let is_local = this.update(&mut cx, |this, _| this.is_local())?;
let is_local = this.read(&cx, |this, _| this.is_local());
for change in changes { for change in changes {
match change { match change {
@ -2224,7 +2223,7 @@ impl Project {
BufferOrderedMessage::Resync => { BufferOrderedMessage::Resync => {
operations_by_buffer_id.clear(); operations_by_buffer_id.clear();
if this if this
.update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx)) .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))?
.await .await
.is_ok() .is_ok()
{ {
@ -2241,11 +2240,11 @@ impl Project {
&mut operations_by_buffer_id, &mut operations_by_buffer_id,
&mut needs_resync_with_host, &mut needs_resync_with_host,
is_local, is_local,
&cx, &mut cx,
) )
.await; .await;
this.read(&cx, |this, _| { this.update(&mut cx, |this, _| {
if let Some(project_id) = this.remote_id() { if let Some(project_id) = this.remote_id() {
this.client this.client
.send(proto::UpdateLanguageServer { .send(proto::UpdateLanguageServer {
@ -2265,12 +2264,12 @@ impl Project {
&mut operations_by_buffer_id, &mut operations_by_buffer_id,
&mut needs_resync_with_host, &mut needs_resync_with_host,
is_local, is_local,
&cx, &mut cx,
) )
.await; .await;
} }
None Ok(())
} }
fn on_buffer_event( fn on_buffer_event(
@ -2432,7 +2431,7 @@ impl Project {
const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration = const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration =
Duration::from_secs(1); Duration::from_secs(1);
let task = cx.spawn_weak(|this, mut cx| async move { let task = cx.spawn(|this, mut cx| async move {
cx.executor().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await; cx.executor().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
if let Some(this) = this.upgrade(&cx) { if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
@ -7065,7 +7064,7 @@ impl Project {
} }
let buffer_id = state.id; let buffer_id = state.id;
let buffer = cx.add_model(|_| { let buffer = cx.entity(|_| {
Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap() Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap()
}); });
this.incomplete_remote_buffers this.incomplete_remote_buffers
@ -7169,7 +7168,7 @@ impl Project {
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<proto::BufferSaved> { ) -> Result<proto::BufferSaved> {
let buffer_id = envelope.payload.buffer_id; let buffer_id = envelope.payload.buffer_id;
let (project_id, buffer) = this.update(&mut cx, |this, cx| { let (project_id, buffer) = this.update(&mut cx, |this, _cx| {
let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?; let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?;
let buffer = this let buffer = this
.opened_buffers .opened_buffers
@ -7339,12 +7338,12 @@ impl Project {
} }
let trigger = FormatTrigger::from_proto(envelope.payload.trigger); let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx)) Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
})?; })??;
let project_transaction = format.await?; let project_transaction = format.await?;
let project_transaction = this.update(&mut cx, |this, cx| { let project_transaction = this.update(&mut cx, |this, cx| {
this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx) this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
}); })?;
Ok(proto::FormatBuffersResponse { Ok(proto::FormatBuffersResponse {
transaction: Some(project_transaction), transaction: Some(project_transaction),
}) })
@ -7371,13 +7370,13 @@ impl Project {
language.cloned(), language.cloned(),
); );
Ok::<_, anyhow::Error>((buffer, completion)) Ok::<_, anyhow::Error>((buffer, completion))
})?; })??;
let completion = completion.await?; let completion = completion.await?;
let apply_additional_edits = this.update(&mut cx, |this, cx| { let apply_additional_edits = this.update(&mut cx, |this, cx| {
this.apply_additional_edits_for_completion(buffer, completion, false, cx) this.apply_additional_edits_for_completion(buffer, completion, false, cx)
}); })?;
Ok(proto::ApplyCompletionAdditionalEditsResponse { Ok(proto::ApplyCompletionAdditionalEditsResponse {
transaction: apply_additional_edits transaction: apply_additional_edits
@ -7407,12 +7406,12 @@ impl Project {
.and_then(|buffer| buffer.upgrade()) .and_then(|buffer| buffer.upgrade())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?; .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx)) Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx))
})?; })??;
let project_transaction = apply_code_action.await?; let project_transaction = apply_code_action.await?;
let project_transaction = this.update(&mut cx, |this, cx| { let project_transaction = this.update(&mut cx, |this, cx| {
this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx) this.serialize_project_transaction_for_peer(project_transaction, sender_id, cx)
}); })?;
Ok(proto::ApplyCodeActionResponse { Ok(proto::ApplyCodeActionResponse {
transaction: Some(project_transaction), transaction: Some(project_transaction),
}) })
@ -7441,7 +7440,7 @@ impl Project {
envelope.payload.trigger.clone(), envelope.payload.trigger.clone(),
cx, cx,
)) ))
})?; })??;
let transaction = on_type_formatting let transaction = on_type_formatting
.await? .await?
@ -7462,13 +7461,13 @@ impl Project {
.get(&envelope.payload.buffer_id) .get(&envelope.payload.buffer_id)
.and_then(|buffer| buffer.upgrade()) .and_then(|buffer| buffer.upgrade())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id)) .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
})?; })??;
let buffer_version = deserialize_version(&envelope.payload.version); let buffer_version = deserialize_version(&envelope.payload.version);
buffer buffer
.update(&mut cx, |buffer, _| { .update(&mut cx, |buffer, _| {
buffer.wait_for_version(buffer_version.clone()) buffer.wait_for_version(buffer_version.clone())
}) })?
.await .await
.with_context(|| { .with_context(|| {
format!( format!(
@ -7557,12 +7556,12 @@ impl Project {
{ {
let sender_id = envelope.original_sender_id()?; let sender_id = envelope.original_sender_id()?;
let buffer_id = T::buffer_id_from_proto(&envelope.payload); let buffer_id = T::buffer_id_from_proto(&envelope.payload);
let buffer_handle = this.read(&cx, |this, _| { let buffer_handle = this.update(&mut cx, |this, _cx| {
this.opened_buffers this.opened_buffers
.get(&buffer_id) .get(&buffer_id)
.and_then(|buffer| buffer.upgrade(&cx)) .and_then(|buffer| buffer.upgrade())
.ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id)) .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
})?; })??;
let request = T::from_proto( let request = T::from_proto(
envelope.payload, envelope.payload,
this.clone(), this.clone(),
@ -7570,11 +7569,11 @@ impl Project {
cx.clone(), cx.clone(),
) )
.await?; .await?;
let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version()); let buffer_version = buffer_handle.update(&mut cx, |buffer, _| buffer.version())?;
let response = this let response = this
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
this.request_lsp(buffer_handle, LanguageServerToQuery::Primary, request, cx) this.request_lsp(buffer_handle, LanguageServerToQuery::Primary, request, cx)
}) })?
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
Ok(T::response_to_proto( Ok(T::response_to_proto(
@ -7584,7 +7583,7 @@ impl Project {
&buffer_version, &buffer_version,
cx, cx,
)) ))
}) })?
} }
async fn handle_get_project_symbols( async fn handle_get_project_symbols(

View file

@ -399,14 +399,14 @@ impl Worktree {
}) })
} }
pub fn remote( pub fn remote<C: Context>(
project_remote_id: u64, project_remote_id: u64,
replica_id: ReplicaId, replica_id: ReplicaId,
worktree: proto::WorktreeMetadata, worktree: proto::WorktreeMetadata,
client: Arc<Client>, client: Arc<Client>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Handle<Self> { ) -> Handle<Self> {
cx.add_model(|cx: &mut ModelContext<Self>| { cx.entity(|cx: &mut ModelContext<Self>| {
let snapshot = Snapshot { let snapshot = Snapshot {
id: WorktreeId(worktree.id as usize), id: WorktreeId(worktree.id as usize),
abs_path: Arc::from(PathBuf::from(worktree.abs_path)), abs_path: Arc::from(PathBuf::from(worktree.abs_path)),