Incremental reparsing for single tokens (WHITESPACE, COMMENT, DOC_COMMENT, IDENT, STRING, RAW_STRING)
This commit is contained in:
parent
b6f8037a6f
commit
4356240fa4
@ -82,22 +82,68 @@ pub fn reparse(&self, edit: &AtomEdit) -> File {
|
||||
self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
|
||||
}
|
||||
pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
|
||||
let (node, green, new_errors) =
|
||||
self.reparse_leaf(&edit).or_else(|| self.reparse_block(&edit))?;
|
||||
|
||||
let green_root = node.replace_with(green);
|
||||
let errors = merge_errors(self.errors(), new_errors, node, edit);
|
||||
Some(File::new(green_root, errors))
|
||||
}
|
||||
fn reparse_leaf(&self, edit: &AtomEdit) -> Option<(SyntaxNodeRef, GreenNode, Vec<SyntaxError>)> {
|
||||
let node = algo::find_covering_node(self.syntax(), edit.delete);
|
||||
match node.kind() {
|
||||
| WHITESPACE
|
||||
| COMMENT
|
||||
| DOC_COMMENT
|
||||
| IDENT
|
||||
| STRING
|
||||
| RAW_STRING => {
|
||||
let text = get_text_after_edit(node, &edit);
|
||||
let tokens = tokenize(&text);
|
||||
if tokens.len() != 1 || tokens[0].kind != node.kind() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let reparser: fn(&mut Parser) = if node.kind().is_trivia() {
|
||||
// since trivia is omitted by parser when it doesn't have a parent, \
|
||||
// we need to create one for it
|
||||
|p| {
|
||||
p.start().complete(p, ROOT);
|
||||
}
|
||||
} else {
|
||||
|p| {
|
||||
p.bump();
|
||||
}
|
||||
};
|
||||
|
||||
let (green, new_errors) =
|
||||
parser_impl::parse_with::<yellow::GreenBuilder>(
|
||||
&text, &tokens, reparser,
|
||||
);
|
||||
|
||||
let green = if node.kind().is_trivia() {
|
||||
green.children().first().cloned().unwrap()
|
||||
} else {
|
||||
green
|
||||
};
|
||||
|
||||
Some((node, green, new_errors))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn reparse_block(&self, edit: &AtomEdit) -> Option<(SyntaxNodeRef, GreenNode, Vec<SyntaxError>)> {
|
||||
let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
|
||||
let text = replace_range(
|
||||
node.text().to_string(),
|
||||
edit.delete - node.range().start(),
|
||||
&edit.insert,
|
||||
);
|
||||
let text = get_text_after_edit(node, &edit);
|
||||
let tokens = tokenize(&text);
|
||||
if !is_balanced(&tokens) {
|
||||
return None;
|
||||
}
|
||||
let (green, new_errors) = parser_impl::parse_with::<yellow::GreenBuilder>(
|
||||
&text, &tokens, reparser,
|
||||
);
|
||||
let green_root = node.replace_with(green);
|
||||
let errors = merge_errors(self.errors(), new_errors, node, edit);
|
||||
Some(File::new(green_root, errors))
|
||||
let (green, new_errors) =
|
||||
parser_impl::parse_with::<yellow::GreenBuilder>(
|
||||
&text, &tokens, reparser,
|
||||
);
|
||||
Some((node, green, new_errors))
|
||||
}
|
||||
fn full_reparse(&self, edit: &AtomEdit) -> File {
|
||||
let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
|
||||
@ -134,6 +180,14 @@ pub fn insert(offset: TextUnit, text: String) -> AtomEdit {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text_after_edit(node: SyntaxNodeRef, edit: &AtomEdit) -> String {
|
||||
replace_range(
|
||||
node.text().to_string(),
|
||||
edit.delete - node.range().start(),
|
||||
&edit.insert,
|
||||
)
|
||||
}
|
||||
|
||||
fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
|
||||
let node = algo::find_covering_node(node, range);
|
||||
return algo::ancestors(node)
|
||||
@ -200,9 +254,9 @@ fn merge_errors(
|
||||
) -> Vec<SyntaxError> {
|
||||
let mut res = Vec::new();
|
||||
for e in old_errors {
|
||||
if e.offset < old_node.range().start() {
|
||||
if e.offset <= old_node.range().start() {
|
||||
res.push(e)
|
||||
} else if e.offset > old_node.range().end() {
|
||||
} else if e.offset >= old_node.range().end() {
|
||||
res.push(SyntaxError {
|
||||
msg: e.msg,
|
||||
offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
|
||||
|
@ -33,7 +33,7 @@ fn do_check(before: &str, replace_with: &str) {
|
||||
let incrementally_reparsed = {
|
||||
let f = File::parse(&before);
|
||||
let edit = AtomEdit { delete: range, insert: replace_with.to_string() };
|
||||
f.incremental_reparse(&edit).unwrap()
|
||||
f.incremental_reparse(&edit).expect("cannot incrementally reparse")
|
||||
};
|
||||
assert_eq_text!(
|
||||
&dump_tree(fully_reparsed.syntax()),
|
||||
@ -45,6 +45,11 @@ fn do_check(before: &str, replace_with: &str) {
|
||||
fn foo() {
|
||||
let x = foo + <|>bar<|>
|
||||
}
|
||||
", "baz");
|
||||
do_check(r"
|
||||
fn foo() {
|
||||
let x = foo<|> + bar<|>
|
||||
}
|
||||
", "baz");
|
||||
do_check(r"
|
||||
struct Foo {
|
||||
@ -67,6 +72,11 @@ fn <|><|>
|
||||
trait Foo {
|
||||
type <|>Foo<|>;
|
||||
}
|
||||
", "Output");
|
||||
do_check(r"
|
||||
trait Foo {
|
||||
type<|> Foo<|>;
|
||||
}
|
||||
", "Output");
|
||||
do_check(r"
|
||||
impl IntoIterator<Item=i32> for Foo {
|
||||
@ -94,6 +104,50 @@ fn foo() {
|
||||
fn<|>;<|>
|
||||
}
|
||||
", " exit(code: c_int)");
|
||||
do_check(r"<|><|>
|
||||
fn foo() -> i32 {
|
||||
1
|
||||
}
|
||||
", "\n\n\n \n");
|
||||
do_check(r"
|
||||
fn foo() -> <|><|> {}
|
||||
", " \n");
|
||||
do_check(r"
|
||||
fn <|>foo<|>() -> i32 {
|
||||
1
|
||||
}
|
||||
", "bar");
|
||||
do_check(r"
|
||||
fn aa<|><|>bb() {
|
||||
|
||||
}
|
||||
", "foofoo");
|
||||
do_check(r"
|
||||
fn aabb /* <|><|> */ () {
|
||||
|
||||
}
|
||||
", "some comment");
|
||||
do_check(r"
|
||||
fn aabb <|><|> () {
|
||||
|
||||
}
|
||||
", " \t\t\n\n");
|
||||
do_check(r"
|
||||
trait foo {
|
||||
// comment <|><|>
|
||||
}
|
||||
", "\n");
|
||||
do_check(r"
|
||||
/// good <|><|>omment
|
||||
mod {
|
||||
}
|
||||
", "c");
|
||||
do_check(r#"
|
||||
fn -> &str { "Hello<|><|>" }
|
||||
"#, ", world");
|
||||
do_check(r#"
|
||||
fn -> &str { // "Hello<|><|>"
|
||||
"#, ", world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user