aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-25 12:45:17 +0100
committerAleksey Kladov <[email protected]>2018-08-25 12:45:17 +0100
commitc3e5987c433cdd0ea95a6b1057b442f4f0fe1ffc (patch)
tree1ef2814a3ddc800ef6976aa0459c6b5cf0c3b621 /crates
parent5211e7d97771aa7f8d7cc99e5131fb3cc71a1627 (diff)
incremental reparse
Diffstat (limited to 'crates')
-rw-r--r--crates/libsyntax2/src/grammar/mod.rs6
-rw-r--r--crates/libsyntax2/src/lib.rs41
-rw-r--r--crates/libsyntax2/src/parser_impl/mod.rs9
-rw-r--r--crates/libsyntax2/src/yellow/syntax.rs23
-rw-r--r--crates/libsyntax2/tests/test/main.rs38
5 files changed, 104 insertions, 13 deletions
diff --git a/crates/libsyntax2/src/grammar/mod.rs b/crates/libsyntax2/src/grammar/mod.rs
index 46ba8a89a..496d28349 100644
--- a/crates/libsyntax2/src/grammar/mod.rs
+++ b/crates/libsyntax2/src/grammar/mod.rs
@@ -40,11 +40,11 @@ pub(crate) use self::{
40 items::named_field_def_list, 40 items::named_field_def_list,
41}; 41};
42 42
43pub(crate) fn file(p: &mut Parser) { 43pub(crate) fn root(p: &mut Parser) {
44 let file = p.start(); 44 let m = p.start();
45 p.eat(SHEBANG); 45 p.eat(SHEBANG);
46 items::mod_contents(p, false); 46 items::mod_contents(p, false);
47 file.complete(p, ROOT); 47 m.complete(p, ROOT);
48} 48}
49 49
50 50
diff --git a/crates/libsyntax2/src/lib.rs b/crates/libsyntax2/src/lib.rs
index bb060cbae..d43d26c4c 100644
--- a/crates/libsyntax2/src/lib.rs
+++ b/crates/libsyntax2/src/lib.rs
@@ -70,13 +70,15 @@ impl File {
70 } 70 }
71 pub fn parse(text: &str) -> File { 71 pub fn parse(text: &str) -> File {
72 let tokens = tokenize(&text); 72 let tokens = tokenize(&text);
73 let (green, errors) = parser_impl::parse::<yellow::GreenBuilder>(text, &tokens); 73 let (green, errors) = parser_impl::parse_with::<yellow::GreenBuilder>(
74 text, &tokens, grammar::root,
75 );
74 File::new(green, errors) 76 File::new(green, errors)
75 } 77 }
76 pub fn reparse(&self, edit: &AtomEdit) -> File { 78 pub fn reparse(&self, edit: &AtomEdit) -> File {
77 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) 79 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
78 } 80 }
79 fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> { 81 pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
80 let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?; 82 let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
81 let text = replace_range( 83 let text = replace_range(
82 node.text(), 84 node.text(),
@@ -87,7 +89,12 @@ impl File {
87 if !is_balanced(&tokens) { 89 if !is_balanced(&tokens) {
88 return None; 90 return None;
89 } 91 }
90 None 92 let (green, new_errors) = parser_impl::parse_with::<yellow::GreenBuilder>(
93 &text, &tokens, reparser,
94 );
95 let green_root = node.replace_with(green);
96 let errors = merge_errors(self.errors(), new_errors, edit, node.range().start());
97 Some(File::new(green_root, errors))
91 } 98 }
92 fn full_reparse(&self, edit: &AtomEdit) -> File { 99 fn full_reparse(&self, edit: &AtomEdit) -> File {
93 let text = replace_range(self.syntax().text(), edit.delete, &edit.insert); 100 let text = replace_range(self.syntax().text(), edit.delete, &edit.insert);
@@ -173,7 +180,7 @@ fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(Syntax
173 } 180 }
174} 181}
175 182
176fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String { 183pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
177 let start = u32::from(range.start()) as usize; 184 let start = u32::from(range.start()) as usize;
178 let end = u32::from(range.end()) as usize; 185 let end = u32::from(range.end()) as usize;
179 text.replace_range(start..end, replace_with); 186 text.replace_range(start..end, replace_with);
@@ -199,3 +206,29 @@ fn is_balanced(tokens: &[Token]) -> bool {
199 } 206 }
200 balance == 0 207 balance == 0
201} 208}
209
210fn merge_errors(
211 old_errors: Vec<SyntaxError>,
212 new_errors: Vec<SyntaxError>,
213 edit: &AtomEdit,
214 node_offset: TextUnit,
215) -> Vec<SyntaxError> {
216 let mut res = Vec::new();
217 for e in old_errors {
218 if e.offset < edit.delete.start() {
219 res.push(e)
220 } else if e.offset > edit.delete.end() {
221 res.push(SyntaxError {
222 msg: e.msg,
223 offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
224 })
225 }
226 }
227 for e in new_errors {
228 res.push(SyntaxError {
229 msg: e.msg,
230 offset: e.offset + node_offset,
231 })
232 }
233 res
234}
diff --git a/crates/libsyntax2/src/parser_impl/mod.rs b/crates/libsyntax2/src/parser_impl/mod.rs
index 14cceced5..f60ef80f0 100644
--- a/crates/libsyntax2/src/parser_impl/mod.rs
+++ b/crates/libsyntax2/src/parser_impl/mod.rs
@@ -2,7 +2,6 @@ mod event;
2mod input; 2mod input;
3 3
4use { 4use {
5 grammar,
6 lexer::Token, 5 lexer::Token,
7 parser_api::Parser, 6 parser_api::Parser,
8 parser_impl::{ 7 parser_impl::{
@@ -27,12 +26,16 @@ pub(crate) trait Sink<'a> {
27} 26}
28 27
29/// Parse a sequence of tokens into the representative node tree 28/// Parse a sequence of tokens into the representative node tree
30pub(crate) fn parse<'a, S: Sink<'a>>(text: &'a str, tokens: &[Token]) -> S::Tree { 29pub(crate) fn parse_with<'a, S: Sink<'a>>(
30 text: &'a str,
31 tokens: &[Token],
32 parser: fn(&mut Parser),
33) -> S::Tree {
31 let events = { 34 let events = {
32 let input = input::ParserInput::new(text, tokens); 35 let input = input::ParserInput::new(text, tokens);
33 let parser_impl = ParserImpl::new(&input); 36 let parser_impl = ParserImpl::new(&input);
34 let mut parser_api = Parser(parser_impl); 37 let mut parser_api = Parser(parser_impl);
35 grammar::file(&mut parser_api); 38 parser(&mut parser_api);
36 parser_api.0.into_events() 39 parser_api.0.into_events()
37 }; 40 };
38 let mut sink = S::new(text); 41 let mut sink = S::new(text);
diff --git a/crates/libsyntax2/src/yellow/syntax.rs b/crates/libsyntax2/src/yellow/syntax.rs
index 8f1b1d79a..0045598d4 100644
--- a/crates/libsyntax2/src/yellow/syntax.rs
+++ b/crates/libsyntax2/src/yellow/syntax.rs
@@ -3,7 +3,7 @@ use std::{fmt, sync::Arc};
3use smol_str::SmolStr; 3use smol_str::SmolStr;
4 4
5use { 5use {
6 yellow::{RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot}, 6 yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot},
7 SyntaxKind::{self, *}, 7 SyntaxKind::{self, *},
8 TextRange, TextUnit, 8 TextRange, TextUnit,
9}; 9};
@@ -141,6 +141,27 @@ impl<R: TreeRoot> SyntaxNode<R> {
141 self.red().green().leaf_text() 141 self.red().green().leaf_text()
142 } 142 }
143 143
144 pub(crate) fn replace_with(&self, green: GreenNode) -> GreenNode {
145 assert_eq!(self.kind(), green.kind());
146 match self.parent() {
147 None => green,
148 Some(parent) => {
149 let children: Vec<_> = parent.children().map(|child| {
150 if child == *self {
151 green.clone()
152 } else {
153 child.red().green().clone()
154 }
155 }).collect();
156 let new_parent = GreenNode::new_branch(
157 parent.kind(),
158 children.into_boxed_slice(),
159 );
160 parent.replace_with(new_parent)
161 },
162 }
163 }
164
144 fn red(&self) -> &RedNode { 165 fn red(&self) -> &RedNode {
145 unsafe { self.red.get(&self.root) } 166 unsafe { self.red.get(&self.root) }
146 } 167 }
diff --git a/crates/libsyntax2/tests/test/main.rs b/crates/libsyntax2/tests/test/main.rs
index cb8a52c98..e7ae4d601 100644
--- a/crates/libsyntax2/tests/test/main.rs
+++ b/crates/libsyntax2/tests/test/main.rs
@@ -9,7 +9,11 @@ use std::{
9 fmt::Write, 9 fmt::Write,
10}; 10};
11 11
12use libsyntax2::File; 12use test_utils::extract_range;
13use libsyntax2::{
14 File, AtomEdit,
15 utils::dump_tree,
16};
13 17
14#[test] 18#[test]
15fn lexer_tests() { 19fn lexer_tests() {
@@ -23,10 +27,40 @@ fn lexer_tests() {
23fn parser_tests() { 27fn parser_tests() {
24 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| { 28 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| {
25 let file = File::parse(text); 29 let file = File::parse(text);
26 libsyntax2::utils::dump_tree(file.syntax()) 30 dump_tree(file.syntax())
27 }) 31 })
28} 32}
29 33
34#[test]
35fn reparse_test() {
36 fn do_check(before: &str, replace_with: &str) {
37 let (range, before) = extract_range(before);
38 let after = libsyntax2::replace_range(before.clone(), range, replace_with);
39
40 let fully_reparsed = File::parse(&after);
41 let incrementally_reparsed = {
42 let f = File::parse(&before);
43 let edit = AtomEdit { delete: range, insert: replace_with.to_string() };
44 f.incremental_reparse(&edit).unwrap()
45 };
46 assert_eq_text!(
47 &dump_tree(fully_reparsed.syntax()),
48 &dump_tree(incrementally_reparsed.syntax()),
49 )
50 }
51
52 do_check(r"
53fn foo() {
54 let x = foo + <|>bar<|>
55}
56", "baz");
57 do_check(r"
58struct Foo {
59 f: foo<|><|>
60}
61", ",\n g: (),");
62}
63
30 64
31/// Read file and normalize newlines. 65/// Read file and normalize newlines.
32/// 66///