aboutsummaryrefslogtreecommitdiff
path: root/crates/libsyntax2
diff options
context:
space:
mode:
authordarksv <[email protected]>2018-09-13 22:25:05 +0100
committerdarksv <[email protected]>2018-09-13 22:25:05 +0100
commit4356240fa42eebbd17fdb63b2d0e6f86387ca21c (patch)
tree96785c930199eac49a9f48cd1d9c67f0f7c1dc4f /crates/libsyntax2
parentb6f8037a6f8fbcb4f127e1d2b518279650b1f5ea (diff)
Incremental reparsing for single tokens (WHITESPACE, COMMENT, DOC_COMMENT, IDENT, STRING, RAW_STRING)
Diffstat (limited to 'crates/libsyntax2')
-rw-r--r--crates/libsyntax2/src/lib.rs80
-rw-r--r--crates/libsyntax2/tests/test/main.rs56
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
183fn 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
137fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> { 191fn 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"
50fn foo() {
51 let x = foo<|> + bar<|>
52}
53", "baz");
54 do_check(r"
50struct Foo { 55struct 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"
77trait Foo {
78 type<|> Foo<|>;
79}
80", "Output");
81 do_check(r"
72impl IntoIterator<Item=i32> for Foo { 82impl 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)");
107do_check(r"<|><|>
108fn foo() -> i32 {
109 1
110}
111", "\n\n\n \n");
112 do_check(r"
113fn foo() -> <|><|> {}
114", " \n");
115 do_check(r"
116fn <|>foo<|>() -> i32 {
117 1
118}
119", "bar");
120do_check(r"
121fn aa<|><|>bb() {
122
123}
124", "foofoo");
125 do_check(r"
126fn aabb /* <|><|> */ () {
127
128}
129", "some comment");
130 do_check(r"
131fn aabb <|><|> () {
132
133}
134", " \t\t\n\n");
135 do_check(r"
136trait foo {
137// comment <|><|>
138}
139", "\n");
140 do_check(r"
141/// good <|><|>omment
142mod {
143}
144", "c");
145 do_check(r#"
146fn -> &str { "Hello<|><|>" }
147"#, ", world");
148 do_check(r#"
149fn -> &str { // "Hello<|><|>"
150"#, ", world");
97} 151}
98 152
99#[test] 153#[test]