Unnamed repository; edit this file 'description' to name the repository.
snippets: Discard placeholder text for the `$0` tabstop
Michael Davis 2025-01-23
parent 032dada · commit cb0f201
-rw-r--r--helix-core/src/snippets/active.rs17
-rw-r--r--helix-core/src/snippets/elaborate.rs9
2 files changed, 25 insertions, 1 deletions
diff --git a/helix-core/src/snippets/active.rs b/helix-core/src/snippets/active.rs
index c5c743cd..98007ab6 100644
--- a/helix-core/src/snippets/active.rs
+++ b/helix-core/src/snippets/active.rs
@@ -252,4 +252,21 @@ mod tests {
snippet.map(edit.changes());
assert!(!snippet.is_valid(&Selection::point(4)))
}
+
+ #[test]
+ fn tabstop_zero_with_placeholder() {
+ // The `$0` tabstop should not have placeholder text. When we receive a snippet like this
+ // (from older versions of clangd for example) we should discard the placeholder text.
+ let snippet = Snippet::parse("sizeof(${0:expression-or-type})").unwrap();
+ let mut doc = Rope::from("\n");
+ let (transaction, _, snippet) = snippet.render(
+ &doc,
+ &Selection::point(0),
+ |_| (0, 0),
+ &mut SnippetRenderCtx::test_ctx(),
+ );
+ assert!(transaction.apply(&mut doc));
+ assert_eq!(doc, "sizeof()\n");
+ assert!(ActiveSnippet::new(snippet).is_none());
+ }
}
diff --git a/helix-core/src/snippets/elaborate.rs b/helix-core/src/snippets/elaborate.rs
index 0fb5fb7b..012d1db7 100644
--- a/helix-core/src/snippets/elaborate.rs
+++ b/helix-core/src/snippets/elaborate.rs
@@ -178,9 +178,16 @@ impl Snippet {
&mut self,
idx: usize,
parent: Option<TabstopIdx>,
- default: Vec<parser::SnippetElement>,
+ mut default: Vec<parser::SnippetElement>,
) -> TabstopIdx {
let idx = TabstopIdx::elaborate(idx);
+ if idx == LAST_TABSTOP_IDX && !default.is_empty() {
+ // Older versions of clangd for example may send a snippet like `${0:placeholder}`
+ // which is considered by VSCode to be a misuse of the `$0` tabstop.
+ log::warn!("Discarding placeholder text for the `$0` tabstop ({default:?}). \
+ The `$0` tabstop signifies the final cursor position and should not include placeholder text.");
+ default.clear();
+ }
let default = self.elaborate(default, Some(idx));
self.tabstops.push(Tabstop {
idx,