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