diff --git a/Cargo.lock b/Cargo.lock index 8f4ebb8b89..986d46d545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1562,6 +1562,7 @@ dependencies = [ "serde", "smallvec", "smol", + "snippet", "sum_tree", "text", "theme", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 2ce10b4d81..77dcbab3df 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -22,7 +22,9 @@ collections = { path = "../collections" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } language = { path = "../language" } +lsp = { path = "../lsp" } project = { path = "../project" } +snippet = { path = "../snippet" } sum_tree = { path = "../sum_tree" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 120ed69af1..ada7d1f9af 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -42,6 +42,7 @@ use postage::watch; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use smol::Timer; +use snippet::Snippet; use std::{ any::TypeId, cmp::{self, Ordering, Reverse}, @@ -1656,10 +1657,22 @@ impl Editor { .matches .get(completion_state.selected_item)?; let completion = completion_state.completions.get(mat.candidate_id)?; + + if completion.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET) { + self.insert_snippet(completion.old_range.clone(), &completion.new_text, cx) + .log_err(); + } else { + self.buffer.update(cx, |buffer, cx| { + buffer.edit_with_autoindent( + [completion.old_range.clone()], + &completion.new_text, + cx, + ); + }); + } + self.buffer.update(cx, |buffer, cx| { - let mut completion = completion.clone(); - // completion. - buffer.apply_completion(completion, cx) + buffer.apply_additional_edits_for_completion(completion.clone(), cx) }) } @@ -1722,6 +1735,42 @@ impl Editor { }) } + pub fn insert_snippet( + &mut self, + range: Range, + text: &str, + cx: &mut ViewContext, + ) -> Result<()> + where + S: Clone + ToOffset, + { + let snippet = Snippet::parse(text)?; + let tabstops = self.buffer.update(cx, |buffer, cx| { + buffer.edit_with_autoindent([range.clone()], snippet.text, cx); + let snapshot = buffer.read(cx); + let start = range.start.to_offset(&snapshot); + snippet + .tabstops + .iter() + .map(|ranges| { + ranges + .into_iter() + .map(|range| { + snapshot.anchor_before(start + range.start) + ..snapshot.anchor_after(start + range.end) + }) + .collect::>() + }) + .collect::>() + }); + + if let Some(tabstop) = tabstops.first() { + self.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit), cx); + } + + Ok(()) + } + pub fn clear(&mut self, cx: &mut ViewContext) { self.start_transaction(cx); self.select_all(&SelectAll, cx); @@ -6581,6 +6630,23 @@ mod tests { }); } + #[gpui::test] + async fn test_snippets(mut cx: gpui::TestAppContext) { + let settings = cx.read(EditorSettings::test); + + let text = "a. b"; + let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx)); + + editor.update(&mut cx, |editor, cx| { + editor + .insert_snippet(2..2, "f(${1:one}, ${2:two})$0", cx) + .unwrap(); + assert_eq!(editor.text(cx), "a.f(one, two) b"); + assert_eq!(editor.selected_ranges::(cx), &[4..7]); + }); + } + #[gpui::test] async fn test_completion(mut cx: gpui::TestAppContext) { let settings = cx.read(EditorSettings::test); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index e9e839726d..024dd4e67a 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -929,7 +929,7 @@ impl MultiBuffer { } } - pub fn apply_completion( + pub fn apply_additional_edits_for_completion( &self, completion: Completion, cx: &mut ModelContext, @@ -941,7 +941,7 @@ impl MultiBuffer { .buffer .clone(); buffer.update(cx, |buffer, cx| { - buffer.apply_completion( + buffer.apply_additional_edits_for_completion( Completion { old_range: completion.old_range.start.text_anchor ..completion.old_range.end.text_anchor, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index eb9f82ea29..187f1b00a9 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1777,13 +1777,11 @@ impl Buffer { } } - pub fn apply_completion( + pub fn apply_additional_edits_for_completion( &mut self, completion: Completion, cx: &mut ModelContext, ) -> Option>> { - self.edit_with_autoindent([completion.old_range], completion.new_text.clone(), cx); - self.file.as_ref()?.as_local()?; let server = self.language_server.as_ref()?.server.clone(); Some(cx.spawn(|this, mut cx| async move {