@@ -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(())
}))
}
@@ -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::<lsp::request::Completion, _, _>(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::<lsp::request::ResolveCompletionItem, _, _>(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, |_| {});