Detailed changes
@@ -22,7 +22,7 @@ use crate::acp::message_editor::{MessageEditor, MessageEditorEvent};
pub struct EntryViewState {
workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
+ project: WeakEntity<Project>,
history_store: Entity<HistoryStore>,
prompt_store: Option<Entity<PromptStore>>,
entries: Vec<Entry>,
@@ -34,7 +34,7 @@ pub struct EntryViewState {
impl EntryViewState {
pub fn new(
workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
+ project: WeakEntity<Project>,
history_store: Entity<HistoryStore>,
prompt_store: Option<Entity<PromptStore>>,
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
@@ -328,7 +328,7 @@ impl Entry {
fn create_terminal(
workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
+ project: WeakEntity<Project>,
terminal: Entity<acp_thread::Terminal>,
window: &mut Window,
cx: &mut App,
@@ -336,9 +336,9 @@ fn create_terminal(
cx.new(|cx| {
let mut view = TerminalView::new(
terminal.read(cx).inner().clone(),
- workspace.clone(),
+ workspace,
None,
- project.downgrade(),
+ project,
window,
cx,
);
@@ -458,7 +458,7 @@ mod tests {
let view_state = cx.new(|_cx| {
EntryViewState::new(
workspace.downgrade(),
- project.clone(),
+ project.downgrade(),
history_store,
None,
Default::default(),
@@ -39,7 +39,6 @@ use zed_actions::agent::Chat;
pub struct MessageEditor {
mention_set: Entity<MentionSet>,
editor: Entity<Editor>,
- project: Entity<Project>,
workspace: WeakEntity<Workspace>,
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
available_commands: Rc<RefCell<Vec<acp::AvailableCommand>>>,
@@ -98,7 +97,7 @@ impl PromptCompletionProviderDelegate for Entity<MessageEditor> {
impl MessageEditor {
pub fn new(
workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
+ project: WeakEntity<Project>,
history_store: Entity<HistoryStore>,
prompt_store: Option<Entity<PromptStore>>,
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
@@ -135,13 +134,8 @@ impl MessageEditor {
editor.register_addon(MessageEditorAddon::new());
editor
});
- let mention_set = cx.new(|_cx| {
- MentionSet::new(
- project.downgrade(),
- history_store.clone(),
- prompt_store.clone(),
- )
- });
+ let mention_set =
+ cx.new(|_cx| MentionSet::new(project, history_store.clone(), prompt_store.clone()));
let completion_provider = Rc::new(PromptCompletionProvider::new(
cx.entity(),
editor.downgrade(),
@@ -199,7 +193,6 @@ impl MessageEditor {
Self {
editor,
- project,
mention_set,
workspace,
prompt_capabilities,
@@ -572,17 +565,18 @@ impl MessageEditor {
let Some(workspace) = self.workspace.upgrade() else {
return;
};
- let path_style = self.project.read(cx).path_style(cx);
+ let project = workspace.read(cx).project().clone();
+ let path_style = project.read(cx).path_style(cx);
let buffer = self.editor.read(cx).buffer().clone();
let Some(buffer) = buffer.read(cx).as_singleton() else {
return;
};
let mut tasks = Vec::new();
for path in paths {
- let Some(entry) = self.project.read(cx).entry_for_path(&path, cx) else {
+ let Some(entry) = project.read(cx).entry_for_path(&path, cx) else {
continue;
};
- let Some(worktree) = self.project.read(cx).worktree_for_id(path.worktree_id, cx) else {
+ let Some(worktree) = project.read(cx).worktree_for_id(path.worktree_id, cx) else {
continue;
};
let abs_path = worktree.read(cx).absolutize(&path.path);
@@ -690,9 +684,13 @@ impl MessageEditor {
window: &mut Window,
cx: &mut Context<Self>,
) {
+ let Some(workspace) = self.workspace.upgrade() else {
+ return;
+ };
+
self.clear(window, cx);
- let path_style = self.project.read(cx).path_style(cx);
+ let path_style = workspace.read(cx).project().read(cx).path_style(cx);
let mut text = String::new();
let mut mentions = Vec::new();
@@ -935,7 +933,7 @@ mod tests {
cx.new(|cx| {
MessageEditor::new(
workspace.downgrade(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -1046,7 +1044,7 @@ mod tests {
cx.new(|cx| {
MessageEditor::new(
workspace_handle.clone(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
prompt_capabilities.clone(),
@@ -1207,7 +1205,7 @@ mod tests {
let message_editor = cx.new(|cx| {
MessageEditor::new(
workspace_handle,
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
prompt_capabilities.clone(),
@@ -1429,7 +1427,7 @@ mod tests {
let message_editor = cx.new(|cx| {
MessageEditor::new(
workspace_handle,
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
prompt_capabilities.clone(),
@@ -1920,7 +1918,7 @@ mod tests {
cx.new(|cx| {
let editor = MessageEditor::new(
workspace.downgrade(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -2025,7 +2023,7 @@ mod tests {
cx.new(|cx| {
let mut editor = MessageEditor::new(
workspace.downgrade(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -2094,7 +2092,7 @@ mod tests {
cx.new(|cx| {
MessageEditor::new(
workspace.downgrade(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -2157,7 +2155,7 @@ mod tests {
let message_editor = cx.new(|cx| {
MessageEditor::new(
workspace_handle,
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -2315,7 +2313,7 @@ mod tests {
let message_editor = cx.new(|cx| {
MessageEditor::new(
workspace_handle,
- project.clone(),
+ project.downgrade(),
history_store.clone(),
None,
Default::default(),
@@ -344,7 +344,7 @@ impl AcpThreadView {
let message_editor = cx.new(|cx| {
let mut editor = MessageEditor::new(
workspace.clone(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
prompt_store.clone(),
prompt_capabilities.clone(),
@@ -369,7 +369,7 @@ impl AcpThreadView {
let entry_view_state = cx.new(|_| {
EntryViewState::new(
workspace.clone(),
- project.clone(),
+ project.downgrade(),
history_store.clone(),
prompt_store.clone(),
prompt_capabilities.clone(),
@@ -14,7 +14,7 @@ use fs::{Fs, RenameOptions};
use futures::{FutureExt, StreamExt, future::Shared};
use gpui::{
App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription,
- Task,
+ Task, WeakEntity,
};
use itertools::Itertools as _;
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
@@ -688,7 +688,7 @@ pub struct TextThread {
_subscriptions: Vec<Subscription>,
telemetry: Option<Arc<Telemetry>>,
language_registry: Arc<LanguageRegistry>,
- project: Option<Entity<Project>>,
+ project: Option<WeakEntity<Project>>,
prompt_builder: Arc<PromptBuilder>,
completion_mode: agent_settings::CompletionMode,
}
@@ -708,7 +708,7 @@ impl EventEmitter<TextThreadEvent> for TextThread {}
impl TextThread {
pub fn local(
language_registry: Arc<LanguageRegistry>,
- project: Option<Entity<Project>>,
+ project: Option<WeakEntity<Project>>,
telemetry: Option<Arc<Telemetry>>,
prompt_builder: Arc<PromptBuilder>,
slash_commands: Arc<SlashCommandWorkingSet>,
@@ -742,7 +742,7 @@ impl TextThread {
language_registry: Arc<LanguageRegistry>,
prompt_builder: Arc<PromptBuilder>,
slash_commands: Arc<SlashCommandWorkingSet>,
- project: Option<Entity<Project>>,
+ project: Option<WeakEntity<Project>>,
telemetry: Option<Arc<Telemetry>>,
cx: &mut Context<Self>,
) -> Self {
@@ -873,7 +873,7 @@ impl TextThread {
language_registry: Arc<LanguageRegistry>,
prompt_builder: Arc<PromptBuilder>,
slash_commands: Arc<SlashCommandWorkingSet>,
- project: Option<Entity<Project>>,
+ project: Option<WeakEntity<Project>>,
telemetry: Option<Arc<Telemetry>>,
cx: &mut Context<Self>,
) -> Self {
@@ -1167,10 +1167,6 @@ impl TextThread {
self.language_registry.clone()
}
- pub fn project(&self) -> Option<Entity<Project>> {
- self.project.clone()
- }
-
pub fn prompt_builder(&self) -> Arc<PromptBuilder> {
self.prompt_builder.clone()
}
@@ -2967,7 +2963,7 @@ impl TextThread {
}
fn update_model_request_usage(&self, amount: u32, limit: UsageLimit, cx: &mut App) {
- let Some(project) = &self.project else {
+ let Some(project) = self.project.as_ref().and_then(|project| project.upgrade()) else {
return;
};
project.read(cx).user_store().update(cx, |user_store, cx| {
@@ -51,7 +51,7 @@ pub struct TextThreadStore {
telemetry: Arc<Telemetry>,
_watch_updates: Task<Option<()>>,
client: Arc<Client>,
- project: Entity<Project>,
+ project: WeakEntity<Project>,
project_is_shared: bool,
client_subscription: Option<client::Subscription>,
_project_subscriptions: Vec<gpui::Subscription>,
@@ -119,10 +119,10 @@ impl TextThreadStore {
],
project_is_shared: false,
client: project.read(cx).client(),
- project: project.clone(),
+ project: project.downgrade(),
prompt_builder,
};
- this.handle_project_shared(project.clone(), cx);
+ this.handle_project_shared(cx);
this.synchronize_contexts(cx);
this.register_context_server_handlers(cx);
this.reload(cx).detach_and_log_err(cx);
@@ -146,7 +146,7 @@ impl TextThreadStore {
telemetry: project.read(cx).client().telemetry().clone(),
_watch_updates: Task::ready(None),
client: project.read(cx).client(),
- project,
+ project: project.downgrade(),
project_is_shared: false,
client_subscription: None,
_project_subscriptions: Default::default(),
@@ -180,8 +180,10 @@ impl TextThreadStore {
) -> Result<proto::OpenContextResponse> {
let context_id = TextThreadId::from_proto(envelope.payload.context_id);
let operations = this.update(&mut cx, |this, cx| {
+ let project = this.project.upgrade().context("project not found")?;
+
anyhow::ensure!(
- !this.project.read(cx).is_via_collab(),
+ !project.read(cx).is_via_collab(),
"only the host contexts can be opened"
);
@@ -211,8 +213,9 @@ impl TextThreadStore {
mut cx: AsyncApp,
) -> Result<proto::CreateContextResponse> {
let (context_id, operations) = this.update(&mut cx, |this, cx| {
+ let project = this.project.upgrade().context("project not found")?;
anyhow::ensure!(
- !this.project.read(cx).is_via_collab(),
+ !project.read(cx).is_via_collab(),
"can only create contexts as the host"
);
@@ -255,8 +258,9 @@ impl TextThreadStore {
mut cx: AsyncApp,
) -> Result<proto::SynchronizeContextsResponse> {
this.update(&mut cx, |this, cx| {
+ let project = this.project.upgrade().context("project not found")?;
anyhow::ensure!(
- !this.project.read(cx).is_via_collab(),
+ !project.read(cx).is_via_collab(),
"only the host can synchronize contexts"
);
@@ -293,8 +297,12 @@ impl TextThreadStore {
})?
}
- fn handle_project_shared(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
- let is_shared = self.project.read(cx).is_shared();
+ fn handle_project_shared(&mut self, cx: &mut Context<Self>) {
+ let Some(project) = self.project.upgrade() else {
+ return;
+ };
+
+ let is_shared = project.read(cx).is_shared();
let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
if is_shared == was_shared {
return;
@@ -309,7 +317,7 @@ impl TextThreadStore {
false
}
});
- let remote_id = self.project.read(cx).remote_id().unwrap();
+ let remote_id = project.read(cx).remote_id().unwrap();
self.client_subscription = self
.client
.subscribe_to_entity(remote_id)
@@ -323,13 +331,13 @@ impl TextThreadStore {
fn handle_project_event(
&mut self,
- project: Entity<Project>,
+ _project: Entity<Project>,
event: &project::Event,
cx: &mut Context<Self>,
) {
match event {
project::Event::RemoteIdChanged(_) => {
- self.handle_project_shared(project, cx);
+ self.handle_project_shared(cx);
}
project::Event::Reshared => {
self.advertise_contexts(cx);
@@ -382,7 +390,10 @@ impl TextThreadStore {
}
pub fn create_remote(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<TextThread>>> {
- let project = self.project.read(cx);
+ let Some(project) = self.project.upgrade() else {
+ return Task::ready(Err(anyhow::anyhow!("project was dropped")));
+ };
+ let project = project.read(cx);
let Some(project_id) = project.remote_id() else {
return Task::ready(Err(anyhow::anyhow!("project was not remote")));
};
@@ -541,7 +552,10 @@ impl TextThreadStore {
text_thread_id: TextThreadId,
cx: &mut Context<Self>,
) -> Task<Result<Entity<TextThread>>> {
- let project = self.project.read(cx);
+ let Some(project) = self.project.upgrade() else {
+ return Task::ready(Err(anyhow::anyhow!("project was dropped")));
+ };
+ let project = project.read(cx);
let Some(project_id) = project.remote_id() else {
return Task::ready(Err(anyhow::anyhow!("project was not remote")));
};
@@ -618,7 +632,10 @@ impl TextThreadStore {
event: &TextThreadEvent,
cx: &mut Context<Self>,
) {
- let Some(project_id) = self.project.read(cx).remote_id() else {
+ let Some(project) = self.project.upgrade() else {
+ return;
+ };
+ let Some(project_id) = project.read(cx).remote_id() else {
return;
};
@@ -652,12 +669,14 @@ impl TextThreadStore {
}
fn advertise_contexts(&self, cx: &App) {
- let Some(project_id) = self.project.read(cx).remote_id() else {
+ let Some(project) = self.project.upgrade() else {
+ return;
+ };
+ let Some(project_id) = project.read(cx).remote_id() else {
return;
};
-
// For now, only the host can advertise their open contexts.
- if self.project.read(cx).is_via_collab() {
+ if project.read(cx).is_via_collab() {
return;
}
@@ -689,7 +708,10 @@ impl TextThreadStore {
}
fn synchronize_contexts(&mut self, cx: &mut Context<Self>) {
- let Some(project_id) = self.project.read(cx).remote_id() else {
+ let Some(project) = self.project.upgrade() else {
+ return;
+ };
+ let Some(project_id) = project.read(cx).remote_id() else {
return;
};
@@ -828,7 +850,10 @@ impl TextThreadStore {
}
fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
- let context_server_store = self.project.read(cx).context_server_store();
+ let Some(project) = self.project.upgrade() else {
+ return;
+ };
+ let context_server_store = project.read(cx).context_server_store();
cx.subscribe(&context_server_store, Self::handle_context_server_event)
.detach();