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
2 changed files with 45 additions and 24 deletions
|
@ -7,7 +7,7 @@ use ra_fmt::leading_indent;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, TypeBoundsOwner},
|
ast::{self, TypeBoundsOwner},
|
||||||
AstNode, Direction, InsertPosition, NodeOrToken, SyntaxElement,
|
AstNode, Direction, InsertPosition, SyntaxElement,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
T,
|
T,
|
||||||
};
|
};
|
||||||
|
@ -27,29 +27,8 @@ impl<N: AstNode> AstEditor<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
||||||
// FIXME: this is both horrible inefficient and gives larger than
|
for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) {
|
||||||
// necessary diff. I bet there's a cool algorithm to diff trees properly.
|
builder.replace(from.text_range(), to.to_string())
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,48 @@ pub enum InsertPosition<T> {
|
||||||
After(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
|
/// Adds specified children (tokens or nodes) to the current node at the
|
||||||
/// specific position.
|
/// specific position.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Add table
Reference in a new issue