@@ -1,3 +1,5 @@
1use anyhow::{anyhow, Result};
3use anyhow::{anyhow, Result};
3use crate::{util::lsp_pos_to_pos, OffsetEncoding};
5use crate::{util::lsp_pos_to_pos, OffsetEncoding};
@@ -54,6 +56,112 @@ pub fn parse(s: &str) -> Result<Snippet<'_>> {
54 parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest))
56 parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest))
59+pub fn into_transaction<'a>(
60+ snippet: Snippet<'a>,
61+ doc: &helix_core::Rope,
62+ selection: &helix_core::Selection,
63+ edit: &lsp_types::TextEdit,
65+ offset_encoding: OffsetEncoding,
66+) -> helix_core::Transaction {
67+ use helix_core::{smallvec, Range, Selection, Transaction};
68+ use SnippetElement::*;
70+ let text = doc.slice(..);
71+ let primary_cursor = selection.primary().cursor(text);
73+ let start_offset = match lsp_pos_to_pos(doc, edit.range.start, offset_encoding) {
74+ Some(start) => start as i128 - primary_cursor as i128,
75+ None => return Transaction::new(doc),
77+ let end_offset = match lsp_pos_to_pos(doc, edit.range.end, offset_encoding) {
78+ Some(end) => end as i128 - primary_cursor as i128,
79+ None => return Transaction::new(doc),
82+ let newline_with_offset = format!(
83+ "{line_ending}{blank:width$}",
84+ width = edit.range.start.character as usize,
88+ let mut insert = String::new();
89+ let mut offset = (primary_cursor as i128 + start_offset) as usize;
90+ let mut tabstops: Vec<Range> = Vec::new();
92+ for element in snippet.elements {
95+ // small optimization to avoid calling replace when it's unnecessary
96+ let text = if text.contains('\n') {
97+ Cow::Owned(text.replace('\n', &newline_with_offset))
101+ offset += text.chars().count();
102+ insert.push_str(&text);
109+ // TODO: variables. For now, fall back to the default, which defaults to "".
110+ let text = r#default.unwrap_or_default();
111+ offset += text.chars().count();
112+ insert.push_str(text);
115+ // TODO: tabstop indexing: 0 is final cursor position. 1,2,.. are positions.
116+ // TODO: merge tabstops with the same index
117+ tabstops.push(Range::point(offset));
122+ } => match value.as_ref() {
123+ // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
124+ // would make this a bit nicer
126+ let len_chars = text.chars().count();
127+ tabstops.push(Range::new(offset, offset + len_chars + 1));
128+ offset += len_chars;
129+ insert.push_str(text);
133+ "Discarding snippet: generating a transaction for placeholder contents {:?} is unimplemented.",
136+ return Transaction::new(doc);
141+ "Discarding snippet: generating a transaction for {:?} is unimplemented.",
144+ return Transaction::new(doc);
149+ let transaction = Transaction::change_by_selection(doc, selection, |range| {
150+ let cursor = range.cursor(text);
152+ (cursor as i128 + start_offset) as usize,
153+ (cursor as i128 + end_offset) as usize,
154+ Some(insert.clone().into()),
158+ if let Some(first) = tabstops.first() {
159+ transaction.with_selection(Selection::new(smallvec![*first], 0))
57mod parser {
165mod parser {
58 use helix_parsec::*;
166 use helix_parsec::*;