Merge #1919
1919: move diff to ra_syntax r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
53a30d9e69
@ -7,7 +7,7 @@
|
||||
use ra_syntax::{
|
||||
algo,
|
||||
ast::{self, TypeBoundsOwner},
|
||||
AstNode, Direction, InsertPosition, NodeOrToken, SyntaxElement,
|
||||
AstNode, Direction, InsertPosition, SyntaxElement,
|
||||
SyntaxKind::*,
|
||||
T,
|
||||
};
|
||||
@ -27,29 +27,8 @@ pub fn new(node: N) -> AstEditor<N>
|
||||
}
|
||||
|
||||
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
||||
// FIXME: this is both horrible inefficient and gives larger than
|
||||
// necessary diff. I bet there's a cool algorithm to diff trees properly.
|
||||
go(builder, self.original_ast.syntax().clone().into(), self.ast().syntax().clone().into());
|
||||
|
||||
fn go(buf: &mut TextEditBuilder, lhs: SyntaxElement, rhs: SyntaxElement) {
|
||||
if lhs.kind() == rhs.kind() && lhs.text_range().len() == rhs.text_range().len() {
|
||||
if match (&lhs, &rhs) {
|
||||
(NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => lhs.text() == rhs.text(),
|
||||
(NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
|
||||
_ => false,
|
||||
} {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) {
|
||||
if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() {
|
||||
for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) {
|
||||
go(buf, lhs, rhs)
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
buf.replace(lhs.text_range(), rhs.to_string())
|
||||
for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) {
|
||||
builder.replace(from.text_range(), to.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,48 @@ pub enum InsertPosition<T> {
|
||||
After(T),
|
||||
}
|
||||
|
||||
/// Finds minimal the diff, which, applied to `from`, will result in `to`.
|
||||
///
|
||||
/// Specifically, returns a map whose keys are descendants of `from` and values
|
||||
/// are descendants of `to`, such that `replace_descendants(from, map) == to`.
|
||||
///
|
||||
/// A trivial solution is a singletom map `{ from: to }`, but this function
|
||||
/// tries to find a more fine-grained diff.
|
||||
pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> FxHashMap<SyntaxElement, SyntaxElement> {
|
||||
let mut buf = FxHashMap::default();
|
||||
// FIXME: this is both horrible inefficient and gives larger than
|
||||
// necessary diff. I bet there's a cool algorithm to diff trees properly.
|
||||
go(&mut buf, from.clone().into(), to.clone().into());
|
||||
return buf;
|
||||
|
||||
fn go(
|
||||
buf: &mut FxHashMap<SyntaxElement, SyntaxElement>,
|
||||
lhs: SyntaxElement,
|
||||
rhs: SyntaxElement,
|
||||
) {
|
||||
if lhs.kind() == rhs.kind() && lhs.text_range().len() == rhs.text_range().len() {
|
||||
if match (&lhs, &rhs) {
|
||||
(NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
|
||||
lhs.green() == rhs.green() || lhs.text() == rhs.text()
|
||||
}
|
||||
(NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
|
||||
_ => false,
|
||||
} {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) {
|
||||
if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() {
|
||||
for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) {
|
||||
go(buf, lhs, rhs)
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
buf.insert(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds specified children (tokens or nodes) to the current node at the
|
||||
/// specific position.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user