Fix issue where renaming a file would not update imports in related files if they are not open (#36681)

Closes #34445

Now we open a multi-buffer consisting of buffers that have updated,
renamed file imports.

Only local is handled, for now.

Release Notes:

- Fixed an issue where renaming a file would not update imports in
related files if they are not already open.
This commit is contained in:
Smit Barmase 2025-08-21 20:19:17 +05:30 committed by GitHub
parent d9ea97ee9c
commit 697a39c251
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 11 deletions

View file

@ -1900,6 +1900,60 @@ impl Editor {
editor.update_lsp_data(false, Some(*buffer_id), window, cx);
}
}
project::Event::EntryRenamed(transaction) => {
let Some(workspace) = editor.workspace() else {
return;
};
let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
else {
return;
};
if active_editor.entity_id() == cx.entity_id() {
let edited_buffers_already_open = {
let other_editors: Vec<Entity<Editor>> = workspace
.read(cx)
.panes()
.iter()
.flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
.filter(|editor| editor.entity_id() != cx.entity_id())
.collect();
transaction.0.keys().all(|buffer| {
other_editors.iter().any(|editor| {
let multi_buffer = editor.read(cx).buffer();
multi_buffer.read(cx).is_singleton()
&& multi_buffer.read(cx).as_singleton().map_or(
false,
|singleton| {
singleton.entity_id() == buffer.entity_id()
},
)
})
})
};
if !edited_buffers_already_open {
let workspace = workspace.downgrade();
let transaction = transaction.clone();
cx.defer_in(window, move |_, window, cx| {
cx.spawn_in(window, async move |editor, cx| {
Self::open_project_transaction(
&editor,
workspace,
transaction,
"Rename".to_string(),
cx,
)
.await
.ok()
})
.detach();
});
}
}
}
_ => {}
},
));
@ -6282,7 +6336,7 @@ impl Editor {
}
pub async fn open_project_transaction(
this: &WeakEntity<Editor>,
editor: &WeakEntity<Editor>,
workspace: WeakEntity<Workspace>,
transaction: ProjectTransaction,
title: String,
@ -6300,7 +6354,7 @@ impl Editor {
if let Some((buffer, transaction)) = entries.first() {
if entries.len() == 1 {
let excerpt = this.update(cx, |editor, cx| {
let excerpt = editor.update(cx, |editor, cx| {
editor
.buffer()
.read(cx)

View file

@ -88,9 +88,18 @@ pub enum BufferStoreEvent {
},
}
#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct ProjectTransaction(pub HashMap<Entity<Buffer>, language::Transaction>);
impl PartialEq for ProjectTransaction {
fn eq(&self, other: &Self) -> bool {
self.0.len() == other.0.len()
&& self.0.iter().all(|(buffer, transaction)| {
other.0.get(buffer).is_some_and(|t| t.id == transaction.id)
})
}
}
impl EventEmitter<BufferStoreEvent> for BufferStore {}
impl RemoteBufferStore {

View file

@ -8762,7 +8762,7 @@ impl LspStore {
(root_path.join(&old_path), root_path.join(&new_path))
};
Self::will_rename_entry(
let _transaction = Self::will_rename_entry(
this.downgrade(),
worktree_id,
&old_abs_path,
@ -9224,7 +9224,7 @@ impl LspStore {
new_path: &Path,
is_dir: bool,
cx: AsyncApp,
) -> Task<()> {
) -> Task<ProjectTransaction> {
let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from);
let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from);
cx.spawn(async move |cx| {
@ -9257,7 +9257,7 @@ impl LspStore {
.log_err()
.flatten()?;
LocalLspStore::deserialize_workspace_edit(
let transaction = LocalLspStore::deserialize_workspace_edit(
this.upgrade()?,
edit,
false,
@ -9265,8 +9265,8 @@ impl LspStore {
cx,
)
.await
.ok();
Some(())
.ok()?;
Some(transaction)
}
});
tasks.push(apply_edit);
@ -9276,11 +9276,17 @@ impl LspStore {
})
.ok()
.flatten();
let mut merged_transaction = ProjectTransaction::default();
for task in tasks {
// Await on tasks sequentially so that the order of application of edits is deterministic
// (at least with regards to the order of registration of language servers)
task.await;
if let Some(transaction) = task.await {
for (buffer, buffer_transaction) in transaction.0 {
merged_transaction.0.insert(buffer, buffer_transaction);
}
}
}
merged_transaction
})
}

View file

@ -327,6 +327,7 @@ pub enum Event {
RevealInProjectPanel(ProjectEntryId),
SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
ExpandedAllForEntry(WorktreeId, ProjectEntryId),
EntryRenamed(ProjectTransaction),
AgentLocationChanged,
}
@ -2119,7 +2120,7 @@ impl Project {
let is_root_entry = self.entry_is_worktree_root(entry_id, cx);
let lsp_store = self.lsp_store().downgrade();
cx.spawn(async move |_, cx| {
cx.spawn(async move |project, cx| {
let (old_abs_path, new_abs_path) = {
let root_path = worktree.read_with(cx, |this, _| this.abs_path())?;
let new_abs_path = if is_root_entry {
@ -2129,7 +2130,7 @@ impl Project {
};
(root_path.join(&old_path), new_abs_path)
};
LspStore::will_rename_entry(
let transaction = LspStore::will_rename_entry(
lsp_store.clone(),
worktree_id,
&old_abs_path,
@ -2145,6 +2146,12 @@ impl Project {
})?
.await?;
project
.update(cx, |_, cx| {
cx.emit(Event::EntryRenamed(transaction));
})
.ok();
lsp_store
.read_with(cx, |this, _| {
this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);