Remove worktree and project notifies (#26244)
This reduces the number of multibuffer syncs from 100,000 to 20,000. Before this change each editor individually observed the project, so literally any project change was amplified by the number of editors you had open. Now editors listen to their buffers instead of the project, and other users of `cx.observe` on the project have been updated to use specific events to reduce churn. Follow up to #26237 Release Notes: - Improved performance of Zed in large repos with lots of file system events. --------- Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
aef84d453a
commit
80fb88520f
12 changed files with 189 additions and 165 deletions
|
@ -9,7 +9,10 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
|
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
|
||||||
use lsp::LanguageServerName;
|
use lsp::LanguageServerName;
|
||||||
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
|
use project::{
|
||||||
|
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
|
||||||
|
ProjectEnvironmentEvent, WorktreeId,
|
||||||
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
||||||
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||||
|
@ -73,7 +76,22 @@ impl ActivityIndicator {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
cx.subscribe(
|
||||||
|
&project.read(cx).lsp_store(),
|
||||||
|
|_, _, event, cx| match event {
|
||||||
|
LspStoreEvent::LanguageServerUpdate { .. } => cx.notify(),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
cx.subscribe(
|
||||||
|
&project.read(cx).environment().clone(),
|
||||||
|
|_, _, event, cx| match event {
|
||||||
|
ProjectEnvironmentEvent::ErrorsUpdated => cx.notify(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
|
||||||
if let Some(auto_updater) = auto_updater.as_ref() {
|
if let Some(auto_updater) = auto_updater.as_ref() {
|
||||||
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
||||||
|
@ -204,7 +222,7 @@ impl ActivityIndicator {
|
||||||
message: error.0.clone(),
|
message: error.0.clone(),
|
||||||
on_click: Some(Arc::new(move |this, window, cx| {
|
on_click: Some(Arc::new(move |this, window, cx| {
|
||||||
this.project.update(cx, |project, cx| {
|
this.project.update(cx, |project, cx| {
|
||||||
project.remove_environment_error(cx, worktree_id);
|
project.remove_environment_error(worktree_id, cx);
|
||||||
});
|
});
|
||||||
window.dispatch_action(Box::new(workspace::OpenLog), cx);
|
window.dispatch_action(Box::new(workspace::OpenLog), cx);
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -104,49 +104,53 @@ impl ContextStore {
|
||||||
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
|
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
|
||||||
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
|
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
|
||||||
|
|
||||||
let this = cx.new(|cx: &mut Context<Self>| {
|
let this =
|
||||||
let context_server_factory_registry =
|
cx.new(|cx: &mut Context<Self>| {
|
||||||
ContextServerFactoryRegistry::default_global(cx);
|
let context_server_factory_registry =
|
||||||
let context_server_manager = cx.new(|cx| {
|
ContextServerFactoryRegistry::default_global(cx);
|
||||||
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
|
let context_server_manager = cx.new(|cx| {
|
||||||
});
|
ContextServerManager::new(
|
||||||
let mut this = Self {
|
context_server_factory_registry,
|
||||||
contexts: Vec::new(),
|
project.clone(),
|
||||||
contexts_metadata: Vec::new(),
|
cx,
|
||||||
context_server_manager,
|
)
|
||||||
context_server_slash_command_ids: HashMap::default(),
|
});
|
||||||
host_contexts: Vec::new(),
|
let mut this = Self {
|
||||||
fs,
|
contexts: Vec::new(),
|
||||||
languages,
|
contexts_metadata: Vec::new(),
|
||||||
slash_commands,
|
context_server_manager,
|
||||||
telemetry,
|
context_server_slash_command_ids: HashMap::default(),
|
||||||
_watch_updates: cx.spawn(|this, mut cx| {
|
host_contexts: Vec::new(),
|
||||||
async move {
|
fs,
|
||||||
while events.next().await.is_some() {
|
languages,
|
||||||
this.update(&mut cx, |this, cx| this.reload(cx))?
|
slash_commands,
|
||||||
.await
|
telemetry,
|
||||||
.log_err();
|
_watch_updates: cx.spawn(|this, mut cx| {
|
||||||
|
async move {
|
||||||
|
while events.next().await.is_some() {
|
||||||
|
this.update(&mut cx, |this, cx| this.reload(cx))?
|
||||||
|
.await
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
anyhow::Ok(())
|
.log_err()
|
||||||
}
|
}),
|
||||||
.log_err()
|
client_subscription: None,
|
||||||
}),
|
_project_subscriptions: vec![
|
||||||
client_subscription: None,
|
cx.subscribe(&project, Self::handle_project_event)
|
||||||
_project_subscriptions: vec![
|
],
|
||||||
cx.observe(&project, Self::handle_project_changed),
|
project_is_shared: false,
|
||||||
cx.subscribe(&project, Self::handle_project_event),
|
client: project.read(cx).client(),
|
||||||
],
|
project: project.clone(),
|
||||||
project_is_shared: false,
|
prompt_builder,
|
||||||
client: project.read(cx).client(),
|
};
|
||||||
project: project.clone(),
|
this.handle_project_shared(project.clone(), cx);
|
||||||
prompt_builder,
|
this.synchronize_contexts(cx);
|
||||||
};
|
this.register_context_server_handlers(cx);
|
||||||
this.handle_project_changed(project.clone(), cx);
|
this.reload(cx).detach_and_log_err(cx);
|
||||||
this.synchronize_contexts(cx);
|
this
|
||||||
this.register_context_server_handlers(cx);
|
})?;
|
||||||
this.reload(cx).detach_and_log_err(cx);
|
|
||||||
this
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(this)
|
Ok(this)
|
||||||
})
|
})
|
||||||
|
@ -288,7 +292,7 @@ impl ContextStore {
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_project_changed(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
|
fn handle_project_shared(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
|
||||||
let is_shared = self.project.read(cx).is_shared();
|
let is_shared = self.project.read(cx).is_shared();
|
||||||
let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
|
let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
|
||||||
if is_shared == was_shared {
|
if is_shared == was_shared {
|
||||||
|
@ -318,11 +322,14 @@ impl ContextStore {
|
||||||
|
|
||||||
fn handle_project_event(
|
fn handle_project_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Entity<Project>,
|
project: Entity<Project>,
|
||||||
event: &project::Event,
|
event: &project::Event,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
|
project::Event::RemoteIdChanged(_) => {
|
||||||
|
self.handle_project_shared(project, cx);
|
||||||
|
}
|
||||||
project::Event::Reshared => {
|
project::Event::Reshared => {
|
||||||
self.advertise_contexts(cx);
|
self.advertise_contexts(cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1250,11 +1250,6 @@ impl Editor {
|
||||||
let mut project_subscriptions = Vec::new();
|
let mut project_subscriptions = Vec::new();
|
||||||
if mode == EditorMode::Full {
|
if mode == EditorMode::Full {
|
||||||
if let Some(project) = project.as_ref() {
|
if let Some(project) = project.as_ref() {
|
||||||
if buffer.read(cx).is_singleton() {
|
|
||||||
project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
|
|
||||||
cx.emit(EditorEvent::TitleChanged);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
project_subscriptions.push(cx.subscribe_in(
|
project_subscriptions.push(cx.subscribe_in(
|
||||||
project,
|
project,
|
||||||
window,
|
window,
|
||||||
|
@ -15442,14 +15437,9 @@ impl Editor {
|
||||||
}
|
}
|
||||||
multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
|
multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
|
||||||
multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
|
multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
|
||||||
multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
|
multi_buffer::Event::FileHandleChanged
|
||||||
cx.emit(EditorEvent::TitleChanged)
|
| multi_buffer::Event::Reloaded
|
||||||
}
|
| multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
|
||||||
// multi_buffer::Event::DiffBaseChanged => {
|
|
||||||
// self.scrollbar_marker_state.dirty = true;
|
|
||||||
// cx.emit(EditorEvent::DiffBaseChanged);
|
|
||||||
// cx.notify();
|
|
||||||
// }
|
|
||||||
multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
|
multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
|
||||||
multi_buffer::Event::DiagnosticsUpdated => {
|
multi_buffer::Event::DiagnosticsUpdated => {
|
||||||
self.refresh_active_diagnostics(cx);
|
self.refresh_active_diagnostics(cx);
|
||||||
|
|
|
@ -121,6 +121,7 @@ pub enum Event {
|
||||||
Discarded,
|
Discarded,
|
||||||
DirtyChanged,
|
DirtyChanged,
|
||||||
DiagnosticsUpdated,
|
DiagnosticsUpdated,
|
||||||
|
BufferDiffChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A diff hunk, representing a range of consequent lines in a multibuffer.
|
/// A diff hunk, representing a range of consequent lines in a multibuffer.
|
||||||
|
@ -253,6 +254,7 @@ impl DiffState {
|
||||||
if let Some(changed_range) = changed_range.clone() {
|
if let Some(changed_range) = changed_range.clone() {
|
||||||
this.buffer_diff_changed(diff, changed_range, cx)
|
this.buffer_diff_changed(diff, changed_range, cx)
|
||||||
}
|
}
|
||||||
|
cx.emit(Event::BufferDiffChanged);
|
||||||
}
|
}
|
||||||
BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
|
BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{path::Path, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{App, AppContext as _, Context, Entity, Task};
|
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
|
||||||
use settings::Settings as _;
|
use settings::Settings as _;
|
||||||
use worktree::WorktreeId;
|
use worktree::WorktreeId;
|
||||||
|
|
||||||
|
@ -19,6 +19,12 @@ pub struct ProjectEnvironment {
|
||||||
environment_error_messages: HashMap<WorktreeId, EnvironmentErrorMessage>,
|
environment_error_messages: HashMap<WorktreeId, EnvironmentErrorMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ProjectEnvironmentEvent {
|
||||||
|
ErrorsUpdated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<ProjectEnvironmentEvent> for ProjectEnvironment {}
|
||||||
|
|
||||||
impl ProjectEnvironment {
|
impl ProjectEnvironment {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
worktree_store: &Entity<WorktreeStore>,
|
worktree_store: &Entity<WorktreeStore>,
|
||||||
|
@ -65,8 +71,13 @@ impl ProjectEnvironment {
|
||||||
self.environment_error_messages.iter()
|
self.environment_error_messages.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_environment_error(&mut self, worktree_id: WorktreeId) {
|
pub(crate) fn remove_environment_error(
|
||||||
|
&mut self,
|
||||||
|
worktree_id: WorktreeId,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
self.environment_error_messages.remove(&worktree_id);
|
self.environment_error_messages.remove(&worktree_id);
|
||||||
|
cx.emit(ProjectEnvironmentEvent::ErrorsUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the project environment, if possible.
|
/// Returns the project environment, if possible.
|
||||||
|
@ -158,8 +169,9 @@ impl ProjectEnvironment {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(error) = error_message {
|
if let Some(error) = error_message {
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.environment_error_messages.insert(worktree_id, error);
|
this.environment_error_messages.insert(worktree_id, error);
|
||||||
|
cx.emit(ProjectEnvironmentEvent::ErrorsUpdated)
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6667,33 +6667,19 @@ impl LspStore {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
lsp::WorkDoneProgress::Report(report) => {
|
lsp::WorkDoneProgress::Report(report) => self.on_lsp_work_progress(
|
||||||
if self.on_lsp_work_progress(
|
language_server_id,
|
||||||
language_server_id,
|
token,
|
||||||
token.clone(),
|
LanguageServerProgress {
|
||||||
LanguageServerProgress {
|
title: None,
|
||||||
title: None,
|
is_disk_based_diagnostics_progress,
|
||||||
is_disk_based_diagnostics_progress,
|
is_cancellable: report.cancellable.unwrap_or(false),
|
||||||
is_cancellable: report.cancellable.unwrap_or(false),
|
message: report.message,
|
||||||
message: report.message.clone(),
|
percentage: report.percentage.map(|p| p as usize),
|
||||||
percentage: report.percentage.map(|p| p as usize),
|
last_update_at: cx.background_executor().now(),
|
||||||
last_update_at: cx.background_executor().now(),
|
},
|
||||||
},
|
cx,
|
||||||
cx,
|
),
|
||||||
) {
|
|
||||||
cx.emit(LspStoreEvent::LanguageServerUpdate {
|
|
||||||
language_server_id,
|
|
||||||
message: proto::update_language_server::Variant::WorkProgress(
|
|
||||||
proto::LspWorkProgress {
|
|
||||||
token,
|
|
||||||
message: report.message,
|
|
||||||
percentage: report.percentage,
|
|
||||||
is_cancellable: report.cancellable,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lsp::WorkDoneProgress::End(_) => {
|
lsp::WorkDoneProgress::End(_) => {
|
||||||
language_server_status.progress_tokens.remove(&token);
|
language_server_status.progress_tokens.remove(&token);
|
||||||
self.on_lsp_work_end(language_server_id, token.clone(), cx);
|
self.on_lsp_work_end(language_server_id, token.clone(), cx);
|
||||||
|
@ -6733,13 +6719,13 @@ impl LspStore {
|
||||||
token: String,
|
token: String,
|
||||||
progress: LanguageServerProgress,
|
progress: LanguageServerProgress,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> bool {
|
) {
|
||||||
|
let mut did_update = false;
|
||||||
if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
|
if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
|
||||||
match status.pending_work.entry(token) {
|
match status.pending_work.entry(token.clone()) {
|
||||||
btree_map::Entry::Vacant(entry) => {
|
btree_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(progress);
|
entry.insert(progress.clone());
|
||||||
cx.notify();
|
did_update = true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
btree_map::Entry::Occupied(mut entry) => {
|
btree_map::Entry::Occupied(mut entry) => {
|
||||||
let entry = entry.get_mut();
|
let entry = entry.get_mut();
|
||||||
|
@ -6748,7 +6734,7 @@ impl LspStore {
|
||||||
{
|
{
|
||||||
entry.last_update_at = progress.last_update_at;
|
entry.last_update_at = progress.last_update_at;
|
||||||
if progress.message.is_some() {
|
if progress.message.is_some() {
|
||||||
entry.message = progress.message;
|
entry.message = progress.message.clone();
|
||||||
}
|
}
|
||||||
if progress.percentage.is_some() {
|
if progress.percentage.is_some() {
|
||||||
entry.percentage = progress.percentage;
|
entry.percentage = progress.percentage;
|
||||||
|
@ -6756,14 +6742,25 @@ impl LspStore {
|
||||||
if progress.is_cancellable != entry.is_cancellable {
|
if progress.is_cancellable != entry.is_cancellable {
|
||||||
entry.is_cancellable = progress.is_cancellable;
|
entry.is_cancellable = progress.is_cancellable;
|
||||||
}
|
}
|
||||||
cx.notify();
|
did_update = true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
if did_update {
|
||||||
|
cx.emit(LspStoreEvent::LanguageServerUpdate {
|
||||||
|
language_server_id,
|
||||||
|
message: proto::update_language_server::Variant::WorkProgress(
|
||||||
|
proto::LspWorkProgress {
|
||||||
|
token,
|
||||||
|
message: progress.message,
|
||||||
|
percentage: progress.percentage.map(|p| p as u32),
|
||||||
|
is_cancellable: Some(progress.is_cancellable),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_lsp_work_end(
|
fn on_lsp_work_end(
|
||||||
|
|
|
@ -22,7 +22,7 @@ mod project_tests;
|
||||||
mod direnv;
|
mod direnv;
|
||||||
mod environment;
|
mod environment;
|
||||||
use buffer_diff::BufferDiff;
|
use buffer_diff::BufferDiff;
|
||||||
pub use environment::EnvironmentErrorMessage;
|
pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent};
|
||||||
use git::Repository;
|
use git::Repository;
|
||||||
pub mod search_history;
|
pub mod search_history;
|
||||||
mod yarn;
|
mod yarn;
|
||||||
|
@ -886,7 +886,6 @@ impl Project {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.subscribe(&ssh, Self::on_ssh_event).detach();
|
cx.subscribe(&ssh, Self::on_ssh_event).detach();
|
||||||
cx.observe(&ssh, |_, _, cx| cx.notify()).detach();
|
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
buffer_ordered_messages_tx: tx,
|
buffer_ordered_messages_tx: tx,
|
||||||
|
@ -1371,9 +1370,9 @@ impl Project {
|
||||||
self.environment.read(cx).environment_errors()
|
self.environment.read(cx).environment_errors()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_environment_error(&mut self, cx: &mut Context<Self>, worktree_id: WorktreeId) {
|
pub fn remove_environment_error(&mut self, worktree_id: WorktreeId, cx: &mut Context<Self>) {
|
||||||
self.environment.update(cx, |environment, _| {
|
self.environment.update(cx, |environment, cx| {
|
||||||
environment.remove_environment_error(worktree_id);
|
environment.remove_environment_error(worktree_id, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1764,7 +1763,6 @@ impl Project {
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.emit(Event::RemoteIdChanged(Some(project_id)));
|
cx.emit(Event::RemoteIdChanged(Some(project_id)));
|
||||||
cx.notify();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1780,7 +1778,6 @@ impl Project {
|
||||||
self.worktree_store.update(cx, |worktree_store, cx| {
|
self.worktree_store.update(cx, |worktree_store, cx| {
|
||||||
worktree_store.send_project_updates(cx);
|
worktree_store.send_project_updates(cx);
|
||||||
});
|
});
|
||||||
cx.notify();
|
|
||||||
cx.emit(Event::Reshared);
|
cx.emit(Event::Reshared);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1810,13 +1807,12 @@ impl Project {
|
||||||
self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
|
self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.emit(Event::Rejoined);
|
cx.emit(Event::Rejoined);
|
||||||
cx.notify();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unshare(&mut self, cx: &mut Context<Self>) -> Result<()> {
|
pub fn unshare(&mut self, cx: &mut Context<Self>) -> Result<()> {
|
||||||
self.unshare_internal(cx)?;
|
self.unshare_internal(cx)?;
|
||||||
cx.notify();
|
cx.emit(Event::RemoteIdChanged(None));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1860,7 +1856,6 @@ impl Project {
|
||||||
}
|
}
|
||||||
self.disconnected_from_host_internal(cx);
|
self.disconnected_from_host_internal(cx);
|
||||||
cx.emit(Event::DisconnectedFromHost);
|
cx.emit(Event::DisconnectedFromHost);
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut Context<Self>) {
|
pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut Context<Self>) {
|
||||||
|
@ -2509,15 +2504,11 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_worktree_added(&mut self, worktree: &Entity<Worktree>, cx: &mut Context<Self>) {
|
fn on_worktree_added(&mut self, worktree: &Entity<Worktree>, _: &mut Context<Self>) {
|
||||||
{
|
let mut remotely_created_models = self.remotely_created_models.lock();
|
||||||
let mut remotely_created_models = self.remotely_created_models.lock();
|
if remotely_created_models.retain_count > 0 {
|
||||||
if remotely_created_models.retain_count > 0 {
|
remotely_created_models.worktrees.push(worktree.clone())
|
||||||
remotely_created_models.worktrees.push(worktree.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cx.observe(worktree, |_, _, cx| cx.notify()).detach();
|
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
|
fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut Context<Self>) {
|
||||||
|
@ -2529,8 +2520,6 @@ impl Project {
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_buffer_event(
|
fn on_buffer_event(
|
||||||
|
@ -3804,7 +3793,6 @@ impl Project {
|
||||||
cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
|
cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
|
||||||
this.collaborators
|
this.collaborators
|
||||||
.insert(collaborator.peer_id, collaborator);
|
.insert(collaborator.peer_id, collaborator);
|
||||||
cx.notify();
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3848,7 +3836,6 @@ impl Project {
|
||||||
old_peer_id,
|
old_peer_id,
|
||||||
new_peer_id,
|
new_peer_id,
|
||||||
});
|
});
|
||||||
cx.notify();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
@ -3876,7 +3863,6 @@ impl Project {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.emit(Event::CollaboratorLeft(peer_id));
|
cx.emit(Event::CollaboratorLeft(peer_id));
|
||||||
cx.notify();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
@ -4292,7 +4278,6 @@ impl Project {
|
||||||
worktrees: Vec<proto::WorktreeMetadata>,
|
worktrees: Vec<proto::WorktreeMetadata>,
|
||||||
cx: &mut Context<Project>,
|
cx: &mut Context<Project>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
cx.notify();
|
|
||||||
self.worktree_store.update(cx, |worktree_store, cx| {
|
self.worktree_store.update(cx, |worktree_store, cx| {
|
||||||
worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
|
worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
|
||||||
})
|
})
|
||||||
|
|
|
@ -307,7 +307,7 @@ impl TitleBar {
|
||||||
cx.notify()
|
cx.notify()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
subscriptions.push(cx.observe(&project, |_, _, cx| cx.notify()));
|
subscriptions.push(cx.subscribe(&project, |_, _, _, cx| cx.notify()));
|
||||||
subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx)));
|
subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx)));
|
||||||
subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed));
|
subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed));
|
||||||
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
||||||
|
|
|
@ -852,6 +852,7 @@ impl<T: Item> ItemHandle for Entity<T> {
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let item_id = self.item_id();
|
let item_id = self.item_id();
|
||||||
|
workspace.update_item_dirty_state(self, window, cx);
|
||||||
cx.observe_release_in(self, window, move |workspace, _, _, _| {
|
cx.observe_release_in(self, window, move |workspace, _, _, _| {
|
||||||
workspace.panes_by_item.remove(&item_id);
|
workspace.panes_by_item.remove(&item_id);
|
||||||
event_subscription.take();
|
event_subscription.take();
|
||||||
|
|
|
@ -879,8 +879,6 @@ impl Workspace {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
cx.observe_in(&project, window, |_, _, _, cx| cx.notify())
|
|
||||||
.detach();
|
|
||||||
cx.subscribe_in(&project, window, move |this, _, event, window, cx| {
|
cx.subscribe_in(&project, window, move |this, _, event, window, cx| {
|
||||||
match event {
|
match event {
|
||||||
project::Event::RemoteIdChanged(_) => {
|
project::Event::RemoteIdChanged(_) => {
|
||||||
|
|
|
@ -1570,7 +1570,6 @@ impl LocalWorktree {
|
||||||
this.update_abs_path_and_refresh(new_path, cx);
|
this.update_abs_path_and_refresh(new_path, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cx.notify();
|
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -5889,14 +5888,21 @@ impl WorktreeModelHandle for Entity<Worktree> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx.condition(&tree, |tree, _| tree.entry_for_path(file_name).is_some())
|
let mut events = cx.events(&tree);
|
||||||
.await;
|
while events.next().await.is_some() {
|
||||||
|
if tree.update(cx, |tree, _| tree.entry_for_path(file_name).is_some()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fs.remove_file(&root_path.join(file_name), Default::default())
|
fs.remove_file(&root_path.join(file_name), Default::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.condition(&tree, |tree, _| tree.entry_for_path(file_name).is_none())
|
while events.next().await.is_some() {
|
||||||
.await;
|
if tree.update(cx, |tree, _| tree.entry_for_path(file_name).is_none()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cx.update(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
cx.update(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||||
.await;
|
.await;
|
||||||
|
@ -5950,19 +5956,22 @@ impl WorktreeModelHandle for Entity<Worktree> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx.condition(&tree, |tree, _| {
|
let mut events = cx.events(&tree);
|
||||||
scan_id_increased(tree, &mut git_dir_scan_id)
|
while events.next().await.is_some() {
|
||||||
})
|
if tree.update(cx, |tree, _| scan_id_increased(tree, &mut git_dir_scan_id)) {
|
||||||
.await;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fs.remove_file(&root_path.join(file_name), Default::default())
|
fs.remove_file(&root_path.join(file_name), Default::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx.condition(&tree, |tree, _| {
|
while events.next().await.is_some() {
|
||||||
scan_id_increased(tree, &mut git_dir_scan_id)
|
if tree.update(cx, |tree, _| scan_id_increased(tree, &mut git_dir_scan_id)) {
|
||||||
})
|
break;
|
||||||
.await;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cx.update(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
cx.update(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||||
.await;
|
.await;
|
||||||
|
|
|
@ -12,6 +12,7 @@ use git::{
|
||||||
},
|
},
|
||||||
GITIGNORE,
|
GITIGNORE,
|
||||||
};
|
};
|
||||||
|
use git2::RepositoryInitOptions;
|
||||||
use gpui::{AppContext as _, BorrowAppContext, Context, Task, TestAppContext};
|
use gpui::{AppContext as _, BorrowAppContext, Context, Task, TestAppContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::stream::Stream;
|
use postage::stream::Stream;
|
||||||
|
@ -855,7 +856,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
|
||||||
"ignored-dir": {}
|
"ignored-dir": {}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let tree = Worktree::local(
|
let worktree = Worktree::local(
|
||||||
dir.path(),
|
dir.path(),
|
||||||
true,
|
true,
|
||||||
Arc::new(RealFs::default()),
|
Arc::new(RealFs::default()),
|
||||||
|
@ -868,32 +869,34 @@ async fn test_write_file(cx: &mut TestAppContext) {
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
fs::fs_watcher::global(|_| {}).unwrap();
|
fs::fs_watcher::global(|_| {}).unwrap();
|
||||||
|
|
||||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
cx.read(|cx| worktree.read(cx).as_local().unwrap().scan_complete())
|
||||||
.await;
|
.await;
|
||||||
tree.flush_fs_events(cx).await;
|
worktree.flush_fs_events(cx).await;
|
||||||
|
|
||||||
tree.update(cx, |tree, cx| {
|
worktree
|
||||||
tree.write_file(
|
.update(cx, |tree, cx| {
|
||||||
Path::new("tracked-dir/file.txt"),
|
tree.write_file(
|
||||||
"hello".into(),
|
Path::new("tracked-dir/file.txt"),
|
||||||
Default::default(),
|
"hello".into(),
|
||||||
cx,
|
Default::default(),
|
||||||
)
|
cx,
|
||||||
})
|
)
|
||||||
.await
|
})
|
||||||
.unwrap();
|
.await
|
||||||
tree.update(cx, |tree, cx| {
|
.unwrap();
|
||||||
tree.write_file(
|
worktree
|
||||||
Path::new("ignored-dir/file.txt"),
|
.update(cx, |tree, cx| {
|
||||||
"world".into(),
|
tree.write_file(
|
||||||
Default::default(),
|
Path::new("ignored-dir/file.txt"),
|
||||||
cx,
|
"world".into(),
|
||||||
)
|
Default::default(),
|
||||||
})
|
cx,
|
||||||
.await
|
)
|
||||||
.unwrap();
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
tree.read_with(cx, |tree, _| {
|
worktree.read_with(cx, |tree, _| {
|
||||||
let tracked = tree.entry_for_path("tracked-dir/file.txt").unwrap();
|
let tracked = tree.entry_for_path("tracked-dir/file.txt").unwrap();
|
||||||
let ignored = tree.entry_for_path("ignored-dir/file.txt").unwrap();
|
let ignored = tree.entry_for_path("ignored-dir/file.txt").unwrap();
|
||||||
assert!(!tracked.is_ignored);
|
assert!(!tracked.is_ignored);
|
||||||
|
@ -3349,7 +3352,7 @@ async fn test_conflicted_cherry_pick(cx: &mut TestAppContext) {
|
||||||
.expect("Failed to get HEAD")
|
.expect("Failed to get HEAD")
|
||||||
.peel_to_commit()
|
.peel_to_commit()
|
||||||
.expect("HEAD is not a commit");
|
.expect("HEAD is not a commit");
|
||||||
git_checkout("refs/heads/master", &repo);
|
git_checkout("refs/heads/main", &repo);
|
||||||
std::fs::write(root_path.join("project/a.txt"), "b").unwrap();
|
std::fs::write(root_path.join("project/a.txt"), "b").unwrap();
|
||||||
git_add("a.txt", &repo);
|
git_add("a.txt", &repo);
|
||||||
git_commit("improve letter", &repo);
|
git_commit("improve letter", &repo);
|
||||||
|
@ -3479,7 +3482,9 @@ const MODIFIED: GitSummary = GitSummary {
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn git_init(path: &Path) -> git2::Repository {
|
fn git_init(path: &Path) -> git2::Repository {
|
||||||
git2::Repository::init(path).expect("Failed to initialize git repository")
|
let mut init_opts = RepositoryInitOptions::new();
|
||||||
|
init_opts.initial_head("main");
|
||||||
|
git2::Repository::init_opts(path, &init_opts).expect("Failed to initialize git repository")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue