mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Correct escaping in snippets (#14912)
## Release Notes: - Fixed issue with backslashes not appearing in snippets ([#14721](https://github.com/zed-industries/zed/issues/14721)), motivated by a snippet provided by the latex LSP ([texlab](https://github.com/latex-lsp/texlab)) not working as intended in Zed ([extension issue](https://github.com/rzukic/zed-latex/issues/5)). [Screencast from 2024-07-21 14-57-19.webm](https://github.com/user-attachments/assets/3c95a987-16e5-4132-8c96-15553966d4ac) ## Fix details: Only $, }, \ can be escaped by a backslash as per [LSP spec (under grammar section)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/\#snippet_syntax). Technically, commas and pipes can also be escaped only in "choice" tabstops but it does not look like they are implemented in Zed yet. ## Additional tests added for cases currently not covered: - backslash not being used to escape anything (so just a normal backslash) - backslash escaping a backslash (so that the second does not escape what follows it) --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
83f6a7f228
commit
0ef19dedd2
@ -47,10 +47,20 @@ fn parse_snippet<'a>(
|
|||||||
source = parse_tabstop(&source[1..], text, tabstops)?;
|
source = parse_tabstop(&source[1..], text, tabstops)?;
|
||||||
}
|
}
|
||||||
Some('\\') => {
|
Some('\\') => {
|
||||||
|
// As specified in the LSP spec (`Grammar` section),
|
||||||
|
// backslashes can escape some characters:
|
||||||
|
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax
|
||||||
source = &source[1..];
|
source = &source[1..];
|
||||||
if let Some(c) = source.chars().next() {
|
if let Some(c) = source.chars().next() {
|
||||||
|
if c == '$' || c == '\\' || c == '}' {
|
||||||
text.push(c);
|
text.push(c);
|
||||||
source = &source[c.len_utf8()..];
|
// All escapable characters are 1 byte long:
|
||||||
|
source = &source[1..];
|
||||||
|
} else {
|
||||||
|
text.push('\\');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text.push('\\');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some('}') => {
|
Some('}') => {
|
||||||
@ -197,6 +207,17 @@ mod tests {
|
|||||||
let snippet = Snippet::parse("{a\\}").unwrap();
|
let snippet = Snippet::parse("{a\\}").unwrap();
|
||||||
assert_eq!(snippet.text, "{a}");
|
assert_eq!(snippet.text, "{a}");
|
||||||
assert_eq!(tabstops(&snippet), &[vec![3..3]]);
|
assert_eq!(tabstops(&snippet), &[vec![3..3]]);
|
||||||
|
|
||||||
|
// backslash not functioning as an escape
|
||||||
|
let snippet = Snippet::parse("a\\b").unwrap();
|
||||||
|
assert_eq!(snippet.text, "a\\b");
|
||||||
|
assert_eq!(tabstops(&snippet), &[vec![3..3]]);
|
||||||
|
|
||||||
|
// first backslash cancelling escaping that would
|
||||||
|
// have happened with second backslash
|
||||||
|
let snippet = Snippet::parse("one\\\\$1two").unwrap();
|
||||||
|
assert_eq!(snippet.text, "one\\two");
|
||||||
|
assert_eq!(tabstops(&snippet), &[vec![4..4], vec![7..7]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tabstops(snippet: &Snippet) -> Vec<Vec<Range<isize>>> {
|
fn tabstops(snippet: &Snippet) -> Vec<Vec<Range<isize>>> {
|
||||||
|
Loading…
Reference in New Issue
Block a user