| //! An experimental implementation of [Rust RFC#2256 lrs); |
| let root = SyntaxNode::new_owned(root); |
| validate_block_structure(root.borrowed()); |
| File { root } |
| } |
| pub fn parse(text: &str) -> File { |
| let tokens = tokenize(&text); |
| let (green, errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>( |
| text, &tokens, grammar::root, |
| ); |
| File::new(green, errors) |
| } |
| pub fn reparse(&self, edit: &AtomTextEdit) -> File { |
| self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) |
| } |
| pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<File> { |
| 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 tokens = tokenize(&text); |
| if !is_balanced(&tokens) { |
| return None; |
| } |
| let (green, new_errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>( |
| &te2t, &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)) |
| } |
| fn full_reparse(&self, edit: &AtomTextEdit) -> File { |
| let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert); |
| File::parse(&text) |
| } |
| pub fn ast(&self) -> ast::Root { |
| ast::Root::cast(self.syntax()).unwrap() |
| } |
| pub fn syntax(&self) -> SyntaxNodeRef { |
| self.root.borrowed() |
| } |
| mp_tree(root), |
| ); |
| assert!( |
| node.next_sibling().is_none() && pair.prev_sibling().is_none(), |
| "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n", |
| node, |
| root.text(), |
| node.text(), |
| ); |
| } |
| } |
| _ => (), |
| } |
| } |
| } |
| |
| #[derive(Debug, Clone)] |
| pub struct AtomTextEdit { |
| pub delete: TextRange, |
| pub insert: String, |
| } |
| |
| impl AtomTextEdit { |
| pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { |
| AtomTextEdit { delete: range, insert: replace_with } |
| } |
| |
| pub fn delete(range: TextRange) -> AtomTextEdit { |
| AtomTextEdit::replace(range, String::new()) |
| } |
| |
| pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit { |
| AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text) |
| } |
| } |
| |
| 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) |
| .filter_map(|node| reparser(node).map(|r| (node, r))) |
| .next(); |
| |
| fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> { |
| let res = match node.kind() { |
| BLOCK => grammar::block, |
| RECORD_FIELD_LIST => grammar::record_field_list, |
| _ => return None, |
| }; |
| Some(res) |
| } |
| } |
| |
| pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String { |
| let start = u32::from(range.start()) as usize; |
| let end = u32::from(range.end()) as usize; |
| text.replace_range(start..end, replace_with); |
| text |
| } |
| |
| fn is_balanced(tokens: &[Token]) -> bool { |
| if tokens.is_empty() |
| || tokens.first().unwrap().kind != L_CURLY |
| || tokens.last().unwrap().kind != R_CURLY { |
| return false |
| } |
| let mut balance = 0usize; |
| for t in tokens.iter() { |
| match t.kind { |
| L_CURLYt { |
| pub delete: TextRange, |
| pub insert: String, |
| } |
| |
| impl AtomTextEdit { |
| pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { |
| AtomTextEdit { delete: range, insert: replace_with } |
| } |
| |
| pub fn delete(range: TextRange) -> AtomTextEdit { |
| AtomTextEdit::replace(range, String::new()) |
| } |
| |
| pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit { |
| AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text) |
| } |
| } |
| |
| 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) |
| .filter_map(|node| reparser(node).map(|r| (node, r))) |
| .next(); |
| |
| fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> { |
| let res = match node.kind() { |
| ; |
| let end = u32::from(range.end()) as usize; |
| text.replaT => grammar::record_field_list, |
| _ => return None, |
| }; |
| Some(res) |
| } |
| } |
| |
| pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String { |
| let start = u32::from(range.start()) as usize; |
| let end = u32::from(range.end()) as usize; |
| text.replace_range(start..end, replace_with); |
| text |
| } |
| |
| fn is_balanced(tokens: &[Token]) -> bool { |
| if tokens.is_empty() |
| || tokens.first().unwrap().kind != L_CURLY |
| || tokens.last().unwrap().kind != R_CURLY { |
| return false |
| } |
| let mut balance = 0usize; |
| for t in tokens.iter() { |
| match t.kind { |
| L_CURLY => balance += 1, |
| R_CURLY => balance = match balance.checked_sub(1) { |
| Some(b) => b, |
| None => return false, |
| }, |
| _ => (), |
| } |
| } |
| balance == 0 |
| } |
| |
| fn merge_errors( |
| old_errors: Vec<SyntaxError>, |
| new_errors: Vec<SyntaxError>, |
| old_node: SyntaxNodeRef, |
| edit: &AtomTextEdit, |
| ) -> Vec<SyntaxError> { |
| let mut res = Vec::new(); |
| for e in old_errors { |
| if e.offset < old_node.range().start() { |
| res.push(e) |
| } 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(), |
| }) |
| } |
| } |
| for e in new_errors { |
| res.push(SyntaxError { |
| msg: e.msg, |
| offset: e.offset + old_node.range().start(), |
| }) |
| } |
| res |
| } |