aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/parsing/reparsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/parsing/reparsing.rs')
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs83
1 files changed, 66 insertions, 17 deletions
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index a86da0675..2d65b91f1 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -27,8 +27,8 @@ pub(crate) fn incremental_reparse(
27 edit: &AtomTextEdit, 27 edit: &AtomTextEdit,
28 errors: Vec<SyntaxError>, 28 errors: Vec<SyntaxError>,
29) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 29) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
30 if let Some((green, old_range)) = reparse_token(node, &edit) { 30 if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) {
31 return Some((green, merge_errors(errors, Vec::new(), old_range, edit), old_range)); 31 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
32 } 32 }
33 33
34 if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) { 34 if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) {
@@ -40,7 +40,7 @@ pub(crate) fn incremental_reparse(
40fn reparse_token<'node>( 40fn reparse_token<'node>(
41 root: &'node SyntaxNode, 41 root: &'node SyntaxNode,
42 edit: &AtomTextEdit, 42 edit: &AtomTextEdit,
43) -> Option<(GreenNode, TextRange)> { 43) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
44 let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); 44 let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
45 let prev_token_kind = prev_token.kind(); 45 let prev_token_kind = prev_token.kind();
46 match prev_token_kind { 46 match prev_token_kind {
@@ -54,7 +54,7 @@ fn reparse_token<'node>(
54 } 54 }
55 55
56 let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit); 56 let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit);
57 let (new_token_kind, _error) = lex_single_syntax_kind(&new_text)?; 57 let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?;
58 58
59 if new_token_kind != prev_token_kind 59 if new_token_kind != prev_token_kind
60 || (new_token_kind == IDENT && is_contextual_kw(&new_text)) 60 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
@@ -76,7 +76,11 @@ fn reparse_token<'node>(
76 76
77 let new_token = 77 let new_token =
78 GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into()); 78 GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
79 Some((prev_token.replace_with(new_token), prev_token.text_range())) 79 Some((
80 prev_token.replace_with(new_token),
81 new_err.into_iter().collect(),
82 prev_token.text_range(),
83 ))
80 } 84 }
81 _ => None, 85 _ => None,
82 } 86 }
@@ -87,7 +91,7 @@ fn reparse_block<'node>(
87 edit: &AtomTextEdit, 91 edit: &AtomTextEdit,
88) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 92) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
89 let (node, reparser) = find_reparsable_node(root, edit.delete)?; 93 let (node, reparser) = find_reparsable_node(root, edit.delete)?;
90 let text = get_text_after_edit(node.clone().into(), &edit); 94 let text = get_text_after_edit(node.clone().into(), edit);
91 95
92 let (tokens, new_lexer_errors) = tokenize(&text); 96 let (tokens, new_lexer_errors) = tokenize(&text);
93 if !is_balanced(&tokens) { 97 if !is_balanced(&tokens) {
@@ -162,20 +166,26 @@ fn is_balanced(tokens: &[Token]) -> bool {
162fn merge_errors( 166fn merge_errors(
163 old_errors: Vec<SyntaxError>, 167 old_errors: Vec<SyntaxError>,
164 new_errors: Vec<SyntaxError>, 168 new_errors: Vec<SyntaxError>,
165 old_range: TextRange, 169 range_before_reparse: TextRange,
166 edit: &AtomTextEdit, 170 edit: &AtomTextEdit,
167) -> Vec<SyntaxError> { 171) -> Vec<SyntaxError> {
168 let mut res = Vec::new(); 172 let mut res = Vec::new();
169 for e in old_errors { 173
170 if e.offset() <= old_range.start() { 174 for old_err in old_errors {
171 res.push(e) 175 let old_err_range = old_err.range();
172 } else if e.offset() >= old_range.end() { 176 if old_err_range.end() <= range_before_reparse.start() {
173 res.push(e.add_offset(TextUnit::of_str(&edit.insert), edit.delete.len())); 177 res.push(old_err);
178 } else if old_err_range.start() >= range_before_reparse.end() {
179 let inserted_len = TextUnit::of_str(&edit.insert);
180 res.push(old_err.with_range((old_err_range + inserted_len) - edit.delete.len()));
181 // Note: extra parens are intentional to prevent uint underflow, HWAB (here was a bug)
174 } 182 }
175 } 183 }
176 for e in new_errors { 184 res.extend(new_errors.into_iter().map(|new_err| {
177 res.push(e.add_offset(old_range.start(), 0.into())); 185 // fighting borrow checker with a variable ;)
178 } 186 let offseted_range = new_err.range() + range_before_reparse.start();
187 new_err.with_range(offseted_range)
188 }));
179 res 189 res
180} 190}
181 191
@@ -193,9 +203,9 @@ mod tests {
193 203
194 let fully_reparsed = SourceFile::parse(&after); 204 let fully_reparsed = SourceFile::parse(&after);
195 let incrementally_reparsed: Parse<SourceFile> = { 205 let incrementally_reparsed: Parse<SourceFile> = {
196 let f = SourceFile::parse(&before); 206 let before = SourceFile::parse(&before);
197 let (green, new_errors, range) = 207 let (green, new_errors, range) =
198 incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap(); 208 incremental_reparse(before.tree().syntax(), &edit, before.errors.to_vec()).unwrap();
199 assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length"); 209 assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
200 Parse::new(green, new_errors) 210 Parse::new(green, new_errors)
201 }; 211 };
@@ -204,6 +214,7 @@ mod tests {
204 &format!("{:#?}", fully_reparsed.tree().syntax()), 214 &format!("{:#?}", fully_reparsed.tree().syntax()),
205 &format!("{:#?}", incrementally_reparsed.tree().syntax()), 215 &format!("{:#?}", incrementally_reparsed.tree().syntax()),
206 ); 216 );
217 assert_eq!(fully_reparsed.errors(), incrementally_reparsed.errors());
207 } 218 }
208 219
209 #[test] // FIXME: some test here actually test token reparsing 220 #[test] // FIXME: some test here actually test token reparsing
@@ -402,4 +413,42 @@ enum Foo {
402 4, 413 4,
403 ); 414 );
404 } 415 }
416
417 #[test]
418 fn reparse_str_token_with_error_unchanged() {
419 do_check(r#""<|>Unclosed<|> string literal"#, "Still unclosed", 24);
420 }
421
422 #[test]
423 fn reparse_str_token_with_error_fixed() {
424 do_check(r#""unterinated<|><|>"#, "\"", 12);
425 }
426
427 #[test]
428 fn reparse_block_with_error_in_middle_unchanged() {
429 do_check(
430 r#"fn main() {
431 if {}
432 32 + 4<|><|>
433 return
434 if {}
435 }"#,
436 "23",
437 105,
438 )
439 }
440
441 #[test]
442 fn reparse_block_with_error_in_middle_fixed() {
443 do_check(
444 r#"fn main() {
445 if {}
446 32 + 4<|><|>
447 return
448 if {}
449 }"#,
450 ";",
451 105,
452 )
453 }
405} 454}