1use std::ops::Range;
10
11use parser::{Edition, Reparser};
12
13use crate::{
14 SyntaxError,
15 SyntaxKind::*,
16 T, TextRange, TextSize,
17 parsing::build_tree,
18 syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
19};
20
21pub(crate) fn incremental_reparse(
22 node: &SyntaxNode,
23 delete: TextRange,
24 insert: &str,
25 errors: impl IntoIterator<Item = SyntaxError>,
26 edition: Edition,
27) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
28 if let Some((green, new_errors, old_range)) = reparse_token(node, delete, insert, edition) {
29 return Some((
30 green,
31 merge_errors(errors, new_errors, old_range, delete, insert),
32 old_range,
33 ));
34 }
35
36 if let Some((green, new_errors, old_range)) = reparse_block(node, delete, insert, edition) {
37 return Some((
38 green,
39 merge_errors(errors, new_errors, old_range, delete, insert),
40 old_range,
41 ));
42 }
43 None
44}
45
46fn reparse_token(
47 root: &SyntaxNode,
48 delete: TextRange,
49 insert: &str,
50 edition: Edition,
51) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
52 let prev_token = root.covering_element(delete).as_token()?.clone();
53 let prev_token_kind = prev_token.kind();
54 match prev_token_kind {
55 WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => {
56 if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
57 let deleted_range = delete - prev_token.text_range().start();
59 if prev_token.text()[deleted_range].contains('\n') {
60 return None;
61 }
62 }
63
64 let mut new_text = get_text_after_edit(prev_token.clone().into(), delete, insert);
65 let (new_token_kind, new_err) = parser::LexedStr::single_token(edition, &new_text)?;
66
67 if new_token_kind != prev_token_kind
68 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
69 {
70 return None;
71 }
72
73 if let Some(next_char) = root.text().char_at(prev_token.text_range().end()) {
77 new_text.push(next_char);
78 let token_with_next_char = parser::LexedStr::single_token(edition, &new_text);
79 if let Some((_kind, _error)) = token_with_next_char {
80 return None;
81 }
82 new_text.pop();
83 }
84
85 let new_token = GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), &new_text);
86 let range = TextRange::up_to(TextSize::of(&new_text));
87 Some((
88 prev_token.replace_with(new_token),
89 new_err.into_iter().map(|msg| SyntaxError::new(msg, range)).collect(),
90 prev_token.text_range(),
91 ))
92 }
93 _ => None,
94 }
95}
96
97fn reparse_block(
98 root: &SyntaxNode,
99 delete: TextRange,
100 insert: &str,
101 edition: parser::Edition,
102) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
103 let (node, reparser) = find_reparsable_node(root, delete)?;
104 let text = get_text_after_edit(node.clone().into(), delete, insert);
105
106 let lexed = parser::LexedStr::new(edition, text.as_str());
107 let parser_input = lexed.to_input(edition);
108 if !is_balanced(&lexed) {
109 return None;
110 }
111
112 let tree_traversal = reparser.parse(&parser_input);
113
114 let (green, new_parser_errors, _eof) = build_tree(lexed, tree_traversal);
115
116 Some((node.replace_with(green), new_parser_errors, node.text_range()))
117}
118
119fn get_text_after_edit(element: SyntaxElement, mut delete: TextRange, insert: &str) -> String {
120 delete -= element.text_range().start();
121
122 let mut text = match element {
123 NodeOrToken::Token(token) => token.text().to_owned(),
124 NodeOrToken::Node(node) => node.text().to_string(),
125 };
126 text.replace_range(Range::<usize>::from(delete), insert);
127 text
128}
129
130fn is_contextual_kw(text: &str) -> bool {
131 matches!(text, "auto" | "default" | "union")
132}
133
134fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
135 let node = node.covering_element(range);
136
137 node.ancestors().find_map(|node| {
138 let first_child = node.first_child_or_token().map(|it| it.kind());
139 let parent = node.parent().map(|it| it.kind());
140 Reparser::for_node(node.kind(), first_child, parent).map(|r| (node, r))
141 })
142}
143
144fn is_balanced(lexed: &parser::LexedStr<'_>) -> bool {
145 if lexed.is_empty() || lexed.kind(0) != T!['{'] || lexed.kind(lexed.len() - 1) != T!['}'] {
146 return false;
147 }
148 let mut balance = 0usize;
149 for i in 1..lexed.len() - 1 {
150 match lexed.kind(i) {
151 T!['{'] => balance += 1,
152 T!['}'] => {
153 balance = match balance.checked_sub(1) {
154 Some(b) => b,
155 None => return false,
156 }
157 }
158 _ => (),
159 }
160 }
161 balance == 0
162}
163
164fn merge_errors(
165 old_errors: impl IntoIterator<Item = SyntaxError>,
166 new_errors: Vec<SyntaxError>,
167 range_before_reparse: TextRange,
168 delete: TextRange,
169 insert: &str,
170) -> Vec<SyntaxError> {
171 let mut res = Vec::new();
172
173 for old_err in old_errors {
174 let old_err_range = old_err.range();
175 if old_err_range.end() <= range_before_reparse.start() {
176 res.push(old_err);
177 } else if old_err_range.start() >= range_before_reparse.end() {
178 let inserted_len = TextSize::of(insert);
179 res.push(old_err.with_range((old_err_range + inserted_len) - delete.len()));
180 }
182 }
183 res.extend(new_errors.into_iter().map(|new_err| {
184 let offsetted_range = new_err.range() + range_before_reparse.start();
186 new_err.with_range(offsetted_range)
187 }));
188 res
189}
190
191#[cfg(test)]
192mod tests {
193 use std::ops::Range;
194
195 use parser::Edition;
196 use test_utils::{assert_eq_text, extract_range};
197
198 use super::*;
199 use crate::{AstNode, Parse, SourceFile};
200
201 fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
202 let (range, before) = extract_range(before);
203 let after = {
204 let mut after = before.clone();
205 after.replace_range(Range::<usize>::from(range), replace_with);
206 after
207 };
208
209 let fully_reparsed = SourceFile::parse(&after, Edition::CURRENT);
210 let incrementally_reparsed: Parse<SourceFile> = {
211 let before = SourceFile::parse(&before, Edition::CURRENT);
212 let (green, new_errors, range) = incremental_reparse(
213 before.tree().syntax(),
214 range,
215 replace_with,
216 before.errors.as_deref().unwrap_or_default().iter().cloned(),
217 Edition::CURRENT,
218 )
219 .unwrap();
220 assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
221 Parse::new(green, new_errors)
222 };
223
224 assert_eq_text!(
225 &format!("{:#?}", fully_reparsed.tree().syntax()),
226 &format!("{:#?}", incrementally_reparsed.tree().syntax()),
227 );
228 assert_eq!(fully_reparsed.errors(), incrementally_reparsed.errors());
229 }
230
231 #[test] fn reparse_block_tests() {
233 do_check(
234 r"
235fn foo() {
236 let x = foo + $0bar$0
237}
238",
239 "baz",
240 3,
241 );
242 do_check(
243 r"
244fn foo() {
245 let x = foo$0 + bar$0
246}
247",
248 "baz",
249 25,
250 );
251 do_check(
252 r"
253struct Foo {
254 f: foo$0$0
255}
256",
257 ",\n g: (),",
258 14,
259 );
260 do_check(
261 r"
262fn foo {
263 let;
264 1 + 1;
265 $092$0;
266}
267",
268 "62",
269 31, );
271 do_check(
272 r"
273mod foo {
274 fn $0$0
275}
276",
277 "bar",
278 11,
279 );
280
281 do_check(
282 r"
283trait Foo {
284 type $0Foo$0;
285}
286",
287 "Output",
288 3,
289 );
290 do_check(
291 r"
292impl IntoIterator<Item=i32> for Foo {
293 f$0$0
294}
295",
296 "n next(",
297 9,
298 );
299 do_check(r"use a::b::{foo,$0,bar$0};", "baz", 10);
300 do_check(
301 r"
302pub enum A {
303 Foo$0$0
304}
305",
306 "\nBar;\n",
307 11,
308 );
309 do_check(
310 r"
311foo!{a, b$0$0 d}
312",
313 ", c[3]",
314 8,
315 );
316 do_check(
317 r"
318fn foo() {
319 vec![$0$0]
320}
321",
322 "123",
323 14,
324 );
325 do_check(
326 r"
327extern {
328 fn$0;$0
329}
330",
331 " exit(code: c_int)",
332 11,
333 );
334 }
335
336 #[test]
337 fn reparse_token_tests() {
338 do_check(
339 r"$0$0
340fn foo() -> i32 { 1 }
341",
342 "\n\n\n \n",
343 1,
344 );
345 do_check(
346 r"
347fn foo() -> $0$0 {}
348",
349 " \n",
350 2,
351 );
352 do_check(
353 r"
354fn $0foo$0() -> i32 { 1 }
355",
356 "bar",
357 3,
358 );
359 do_check(
360 r"
361fn foo$0$0foo() { }
362",
363 "bar",
364 6,
365 );
366 do_check(
367 r"
368fn foo /* $0$0 */ () {}
369",
370 "some comment",
371 6,
372 );
373 do_check(
374 r"
375fn baz $0$0 () {}
376",
377 " \t\t\n\n",
378 2,
379 );
380 do_check(
381 r"
382fn baz $0$0 () {}
383",
384 " \t\t\n\n",
385 2,
386 );
387 do_check(
388 r"
389/// foo $0$0omment
390mod { }
391",
392 "c",
393 14,
394 );
395 do_check(
396 r#"
397fn -> &str { "Hello$0$0" }
398"#,
399 ", world",
400 7,
401 );
402 do_check(
403 r#"
404fn -> &str { // "Hello$0$0"
405"#,
406 ", world",
407 10,
408 );
409 do_check(
410 r##"
411fn -> &str { r#"Hello$0$0"#
412"##,
413 ", world",
414 10,
415 );
416 do_check(
417 r"
418#[derive($0Copy$0)]
419enum Foo {
420
421}
422",
423 "Clone",
424 4,
425 );
426 }
427
428 #[test]
429 fn reparse_str_token_with_error_unchanged() {
430 do_check(r#""$0Unclosed$0 string literal"#, "Still unclosed", 24);
431 }
432
433 #[test]
434 fn reparse_str_token_with_error_fixed() {
435 do_check(r#""unterminated$0$0"#, "\"", 13);
436 }
437
438 #[test]
439 fn reparse_block_with_error_in_middle_unchanged() {
440 do_check(
441 r#"fn main() {
442 if {}
443 32 + 4$0$0
444 return
445 if {}
446 }"#,
447 "23",
448 105,
449 )
450 }
451
452 #[test]
453 fn reparse_block_with_error_in_middle_fixed() {
454 do_check(
455 r#"fn main() {
456 if {}
457 32 + 4$0$0
458 return
459 if {}
460 }"#,
461 ";",
462 105,
463 )
464 }
465}