on master help sign in
Add nested placeholder parsing for LSP snippets
0d92425Andrii Grynenko
helix-lsp/src/snippet.rs+49−21
@@ -1,7 +1,7 @@
1use std::borrow::Cow;
1use std::borrow::Cow;
2
2
3use anyhow::{anyhow, Result};
3use anyhow::{anyhow, Result};
4−use helix_core::{SmallVec, smallvec};
4+use helix_core::{smallvec, SmallVec};
5
5
6#[derive(Debug, PartialEq, Eq)]
6#[derive(Debug, PartialEq, Eq)]
7pub enum CaseChange {
7pub enum CaseChange {
@@ -210,8 +210,8 @@ mod parser {
210 }
210 }
211 }
211 }
212
212
213− fn text<'a>() -> impl Parser<'a, Output = &'a str> {
213+ fn text<'a, const SIZE: usize>(cs: [char; SIZE]) -> impl Parser<'a, Output = &'a str> {
214− take_while(|c| c != '$')
214+ take_while(move |c| cs.into_iter().all(|c1| c != c1))
215 }
215 }
216
216
217 fn digit<'a>() -> impl Parser<'a, Output = usize> {
217 fn digit<'a>() -> impl Parser<'a, Output = usize> {
@@ -270,13 +270,15 @@ mod parser {
270 ),
270 ),
271 |seq| { Conditional(seq.1, None, Some(seq.4)) }
271 |seq| { Conditional(seq.1, None, Some(seq.4)) }
272 ),
272 ),
273− // Any text
274− map(text(), Text),
275 )
273 )
276 }
274 }
277
275
278 fn regex<'a>() -> impl Parser<'a, Output = Regex<'a>> {
276 fn regex<'a>() -> impl Parser<'a, Output = Regex<'a>> {
277+ let text = map(text(['$', '/']), FormatItem::Text);
279− let replacement = reparse_as(take_until(|c| c == '/'), one_or_more(format()));
278+ let replacement = reparse_as(
279+ take_until(|c| c == '/'),
280+ one_or_more(choice!(format(), text)),
281+ );
280
282
281 map(
283 map(
282 seq!(
284 seq!(
@@ -306,19 +308,20 @@ mod parser {
306 }
308 }
307
309
308 fn placeholder<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
310 fn placeholder<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
309− // TODO: why doesn't parse_as work?
311+ let text = map(text(['$', '}']), SnippetElement::Text);
310− // let value = reparse_as(take_until(|c| c == '}'), anything());
312+ map(
311− // TODO: fix this to parse nested placeholders (take until terminates too early)
313+ seq!(
312− let value = filter_map(take_until(|c| c == '}'), |s| {
314+ "${",
313− snippet().parse(s).map(|parse_result| parse_result.1).ok()
315+ digit(),
314− });
316+ ":",
315−
317+ one_or_more(choice!(anything(), text)),
316− map(seq!("${", digit(), ":", value, "}"), |seq| {
318+ "}"
319+ ),
317− SnippetElement::Placeholder {
320+ |seq| SnippetElement::Placeholder {
318 tabstop: seq.1,
321 tabstop: seq.1,
319− value: seq.3.elements,
322+ value: seq.3,
320− }
323+ },
321− })
324+ )
322 }
325 }
323
326
324 fn choice<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
327 fn choice<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
@@ -366,12 +369,18 @@ mod parser {
366 }
369 }
367
370
368 fn anything<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
371 fn anything<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> {
369− let text = map(text(), SnippetElement::Text);
372+ // The parser has to be constructed lazily to avoid infinite opaque type recursion
373+ |input: &'a str| {
370− choice!(tabstop(), placeholder(), choice(), variable(), text)
374+ let parser = choice!(tabstop(), placeholder(), choice(), variable());
375+ parser.parse(input)
376+ }
371 }
377 }
372
378
373 fn snippet<'a>() -> impl Parser<'a, Output = Snippet<'a>> {
379 fn snippet<'a>() -> impl Parser<'a, Output = Snippet<'a>> {
380+ let text = map(text(['$']), SnippetElement::Text);
374− map(one_or_more(anything()), |parts| Snippet { elements: parts })
381+ map(one_or_more(choice!(anything(), text)), |parts| Snippet {
382+ elements: parts,
383+ })
375 }
384 }
376
385
377 pub fn parse(s: &str) -> Result<Snippet, &str> {
386 pub fn parse(s: &str) -> Result<Snippet, &str> {
@@ -439,6 +448,25 @@ mod parser {
439 )
448 )
440 }
449 }
441
450
451+ #[test]
452+ fn parse_placeholder_nested_in_placeholder() {
453+ assert_eq!(
454+ Ok(Snippet {
455+ elements: vec![Placeholder {
456+ tabstop: 1,
457+ value: vec!(
458+ Text("foo "),
459+ Placeholder {
460+ tabstop: 2,
461+ value: vec!(Text("bar")),
462+ },
463+ ),
464+ },]
465+ }),
466+ parse("${1:foo ${2:bar}}")
467+ )
468+ }
469+
442 #[test]
470 #[test]
443 fn parse_all() {
471 fn parse_all() {
444 assert_eq!(
472 assert_eq!(