diff options
-rw-r--r-- | crates/libsyntax2/src/lib.rs | 80 | ||||
-rw-r--r-- | crates/libsyntax2/tests/test/main.rs | 56 |
2 files changed, 122 insertions, 14 deletions
diff --git a/crates/libsyntax2/src/lib.rs b/crates/libsyntax2/src/lib.rs index fd58cb4fa..bae685fb4 100644 --- a/crates/libsyntax2/src/lib.rs +++ b/crates/libsyntax2/src/lib.rs | |||
@@ -82,22 +82,68 @@ impl File { | |||
82 | self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) | 82 | self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) |
83 | } | 83 | } |
84 | pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> { | 84 | pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> { |
85 | let (node, green, new_errors) = | ||
86 | self.reparse_leaf(&edit).or_else(|| self.reparse_block(&edit))?; | ||
87 | |||
88 | let green_root = node.replace_with(green); | ||
89 | let errors = merge_errors(self.errors(), new_errors, node, edit); | ||
90 | Some(File::new(green_root, errors)) | ||
91 | } | ||
92 | fn reparse_leaf(&self, edit: &AtomEdit) -> Option<(SyntaxNodeRef, GreenNode, Vec<SyntaxError>)> { | ||
93 | let node = algo::find_covering_node(self.syntax(), edit.delete); | ||
94 | match node.kind() { | ||
95 | | WHITESPACE | ||
96 | | COMMENT | ||
97 | | DOC_COMMENT | ||
98 | | IDENT | ||
99 | | STRING | ||
100 | | RAW_STRING => { | ||
101 | let text = get_text_after_edit(node, &edit); | ||
102 | let tokens = tokenize(&text); | ||
103 | if tokens.len() != 1 || tokens[0].kind != node.kind() { | ||
104 | return None; | ||
105 | } | ||
106 | |||
107 | let reparser: fn(&mut Parser) = if node.kind().is_trivia() { | ||
108 | // since trivia is omitted by parser when it doesn't have a parent, \ | ||
109 | // we need to create one for it | ||
110 | |p| { | ||
111 | p.start().complete(p, ROOT); | ||
112 | } | ||
113 | } else { | ||
114 | |p| { | ||
115 | p.bump(); | ||
116 | } | ||
117 | }; | ||
118 | |||
119 | let (green, new_errors) = | ||
120 | parser_impl::parse_with::<yellow::GreenBuilder>( | ||
121 | &text, &tokens, reparser, | ||
122 | ); | ||
123 | |||
124 | let green = if node.kind().is_trivia() { | ||
125 | green.children().first().cloned().unwrap() | ||
126 | } else { | ||
127 | green | ||
128 | }; | ||
129 | |||
130 | Some((node, green, new_errors)) | ||
131 | }, | ||
132 | _ => None, | ||
133 | } | ||
134 | } | ||
135 | fn reparse_block(&self, edit: &AtomEdit) -> Option<(SyntaxNodeRef, GreenNode, Vec<SyntaxError>)> { | ||
85 | let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?; | 136 | let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?; |
86 | let text = replace_range( | 137 | let text = get_text_after_edit(node, &edit); |
87 | node.text().to_string(), | ||
88 | edit.delete - node.range().start(), | ||
89 | &edit.insert, | ||
90 | ); | ||
91 | let tokens = tokenize(&text); | 138 | let tokens = tokenize(&text); |
92 | if !is_balanced(&tokens) { | 139 | if !is_balanced(&tokens) { |
93 | return None; | 140 | return None; |
94 | } | 141 | } |
95 | let (green, new_errors) = parser_impl::parse_with::<yellow::GreenBuilder>( | 142 | let (green, new_errors) = |
96 | &text, &tokens, reparser, | 143 | parser_impl::parse_with::<yellow::GreenBuilder>( |
97 | ); | 144 | &text, &tokens, reparser, |
98 | let green_root = node.replace_with(green); | 145 | ); |
99 | let errors = merge_errors(self.errors(), new_errors, node, edit); | 146 | Some((node, green, new_errors)) |
100 | Some(File::new(green_root, errors)) | ||
101 | } | 147 | } |
102 | fn full_reparse(&self, edit: &AtomEdit) -> File { | 148 | fn full_reparse(&self, edit: &AtomEdit) -> File { |
103 | let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert); | 149 | let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert); |
@@ -134,6 +180,14 @@ impl AtomEdit { | |||
134 | } | 180 | } |
135 | } | 181 | } |
136 | 182 | ||
183 | fn get_text_after_edit(node: SyntaxNodeRef, edit: &AtomEdit) -> String { | ||
184 | replace_range( | ||
185 | node.text().to_string(), | ||
186 | edit.delete - node.range().start(), | ||
187 | &edit.insert, | ||
188 | ) | ||
189 | } | ||
190 | |||
137 | fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> { | 191 | fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> { |
138 | let node = algo::find_covering_node(node, range); | 192 | let node = algo::find_covering_node(node, range); |
139 | return algo::ancestors(node) | 193 | return algo::ancestors(node) |
@@ -200,9 +254,9 @@ fn merge_errors( | |||
200 | ) -> Vec<SyntaxError> { | 254 | ) -> Vec<SyntaxError> { |
201 | let mut res = Vec::new(); | 255 | let mut res = Vec::new(); |
202 | for e in old_errors { | 256 | for e in old_errors { |
203 | if e.offset < old_node.range().start() { | 257 | if e.offset <= old_node.range().start() { |
204 | res.push(e) | 258 | res.push(e) |
205 | } else if e.offset > old_node.range().end() { | 259 | } else if e.offset >= old_node.range().end() { |
206 | res.push(SyntaxError { | 260 | res.push(SyntaxError { |
207 | msg: e.msg, | 261 | msg: e.msg, |
208 | offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(), | 262 | offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(), |
diff --git a/crates/libsyntax2/tests/test/main.rs b/crates/libsyntax2/tests/test/main.rs index 644df9f3c..ce7e075f8 100644 --- a/crates/libsyntax2/tests/test/main.rs +++ b/crates/libsyntax2/tests/test/main.rs | |||
@@ -33,7 +33,7 @@ fn reparse_test() { | |||
33 | let incrementally_reparsed = { | 33 | let incrementally_reparsed = { |
34 | let f = File::parse(&before); | 34 | let f = File::parse(&before); |
35 | let edit = AtomEdit { delete: range, insert: replace_with.to_string() }; | 35 | let edit = AtomEdit { delete: range, insert: replace_with.to_string() }; |
36 | f.incremental_reparse(&edit).unwrap() | 36 | f.incremental_reparse(&edit).expect("cannot incrementally reparse") |
37 | }; | 37 | }; |
38 | assert_eq_text!( | 38 | assert_eq_text!( |
39 | &dump_tree(fully_reparsed.syntax()), | 39 | &dump_tree(fully_reparsed.syntax()), |
@@ -47,6 +47,11 @@ fn foo() { | |||
47 | } | 47 | } |
48 | ", "baz"); | 48 | ", "baz"); |
49 | do_check(r" | 49 | do_check(r" |
50 | fn foo() { | ||
51 | let x = foo<|> + bar<|> | ||
52 | } | ||
53 | ", "baz"); | ||
54 | do_check(r" | ||
50 | struct Foo { | 55 | struct Foo { |
51 | f: foo<|><|> | 56 | f: foo<|><|> |
52 | } | 57 | } |
@@ -69,6 +74,11 @@ trait Foo { | |||
69 | } | 74 | } |
70 | ", "Output"); | 75 | ", "Output"); |
71 | do_check(r" | 76 | do_check(r" |
77 | trait Foo { | ||
78 | type<|> Foo<|>; | ||
79 | } | ||
80 | ", "Output"); | ||
81 | do_check(r" | ||
72 | impl IntoIterator<Item=i32> for Foo { | 82 | impl IntoIterator<Item=i32> for Foo { |
73 | f<|><|> | 83 | f<|><|> |
74 | } | 84 | } |
@@ -94,6 +104,50 @@ extern { | |||
94 | fn<|>;<|> | 104 | fn<|>;<|> |
95 | } | 105 | } |
96 | ", " exit(code: c_int)"); | 106 | ", " exit(code: c_int)"); |
107 | do_check(r"<|><|> | ||
108 | fn foo() -> i32 { | ||
109 | 1 | ||
110 | } | ||
111 | ", "\n\n\n \n"); | ||
112 | do_check(r" | ||
113 | fn foo() -> <|><|> {} | ||
114 | ", " \n"); | ||
115 | do_check(r" | ||
116 | fn <|>foo<|>() -> i32 { | ||
117 | 1 | ||
118 | } | ||
119 | ", "bar"); | ||
120 | do_check(r" | ||
121 | fn aa<|><|>bb() { | ||
122 | |||
123 | } | ||
124 | ", "foofoo"); | ||
125 | do_check(r" | ||
126 | fn aabb /* <|><|> */ () { | ||
127 | |||
128 | } | ||
129 | ", "some comment"); | ||
130 | do_check(r" | ||
131 | fn aabb <|><|> () { | ||
132 | |||
133 | } | ||
134 | ", " \t\t\n\n"); | ||
135 | do_check(r" | ||
136 | trait foo { | ||
137 | // comment <|><|> | ||
138 | } | ||
139 | ", "\n"); | ||
140 | do_check(r" | ||
141 | /// good <|><|>omment | ||
142 | mod { | ||
143 | } | ||
144 | ", "c"); | ||
145 | do_check(r#" | ||
146 | fn -> &str { "Hello<|><|>" } | ||
147 | "#, ", world"); | ||
148 | do_check(r#" | ||
149 | fn -> &str { // "Hello<|><|>" | ||
150 | "#, ", world"); | ||
97 | } | 151 | } |
98 | 152 | ||
99 | #[test] | 153 | #[test] |