diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6ac1e351886ff3558a5f132ec37790f21b2dea3b..289477da0d57e86ee0bf9b0ef53eaf46b47c0d1f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6684,7 +6684,7 @@ impl Editor { text: new_text[common_prefix_len..].into(), }); - self.transact(window, cx, |editor, window, cx| { + let tx_id = self.transact(window, cx, |editor, window, cx| { if let Some(mut snippet) = snippet { snippet.text = new_text.to_string(); editor @@ -6766,7 +6766,7 @@ impl Editor { } Some(cx.spawn_in(window, async move |editor, cx| { - apply_edits.await?; + let additional_edits_tx = apply_edits.await?; if let Some((lsp_store, command)) = lsp_store.zip(command) { let title = command.lsp_action.title().to_owned(); @@ -6788,6 +6788,18 @@ impl Editor { } } + if let Some(tx_id) = tx_id + && let Some(additional_edits_tx) = additional_edits_tx + { + editor + .update(cx, |editor, cx| { + editor.buffer.update(cx, |buffer, cx| { + buffer.merge_transactions(additional_edits_tx.id, tx_id, cx) + }); + }) + .context("merge transactions")?; + } + Ok(()) })) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a630951735f0f3ddf913503e0c9de0bf0df3fe62..4340c058281ade18fa8d12a51d4e05aba97c46ac 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -20771,6 +20771,103 @@ async fn test_completions_with_additional_edits(cx: &mut TestAppContext) { cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }"); } +#[gpui::test] +async fn test_completions_with_additional_edits_undo(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let mut cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string()]), + resolve_provider: Some(true), + ..Default::default() + }), + ..Default::default() + }, + cx, + ) + .await; + + cx.set_state("fn main() { let a = 2ˇ; }"); + cx.simulate_keystroke("."); + let completion_item = lsp::CompletionItem { + label: "some".into(), + kind: Some(lsp::CompletionItemKind::SNIPPET), + detail: Some("Wrap the expression in an `Option::Some`".to_string()), + documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { + kind: lsp::MarkupKind::Markdown, + value: "```rust\nSome(2)\n```".to_string(), + })), + deprecated: Some(false), + sort_text: Some("fffffff2".to_string()), + filter_text: Some("some".to_string()), + insert_text_format: Some(lsp::InsertTextFormat::SNIPPET), + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range { + start: lsp::Position { + line: 0, + character: 22, + }, + end: lsp::Position { + line: 0, + character: 22, + }, + }, + new_text: "Some(2)".to_string(), + })), + additional_text_edits: Some(vec![lsp::TextEdit { + range: lsp::Range { + start: lsp::Position { + line: 0, + character: 20, + }, + end: lsp::Position { + line: 0, + character: 22, + }, + }, + new_text: "".to_string(), + }]), + ..Default::default() + }; + + let closure_completion_item = completion_item.clone(); + let mut request = cx.set_request_handler::(move |_, _, _| { + let task_completion_item = closure_completion_item.clone(); + async move { + Ok(Some(lsp::CompletionResponse::Array(vec![ + task_completion_item, + ]))) + } + }); + + request.next().await; + + cx.condition(|editor, _| editor.context_menu_visible()) + .await; + let apply_additional_edits = cx.update_editor(|editor, window, cx| { + editor + .confirm_completion(&ConfirmCompletion::default(), window, cx) + .unwrap() + }); + cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }"); + + cx.set_request_handler::(move |_, _, _| { + let task_completion_item = completion_item.clone(); + async move { Ok(task_completion_item) } + }) + .next() + .await + .unwrap(); + apply_additional_edits.await.unwrap(); + cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }"); + + cx.update_editor(|editor, window, cx| { + editor.undo(&crate::Undo, window, cx); + }); + cx.assert_editor_state("fn main() { let a = 2.ˇ; }"); +} + #[gpui::test] async fn test_completions_with_additional_edits_and_multiple_cursors(cx: &mut TestAppContext) { init_test(cx, |_| {});