22
33use std:: {
44 fmt,
5+ hash:: BuildHasherDefault ,
56 ops:: { self , RangeInclusive } ,
67} ;
78
9+ use indexmap:: IndexMap ;
810use itertools:: Itertools ;
911use rustc_hash:: FxHashMap ;
1012use text_edit:: TextEditBuilder ;
@@ -106,42 +108,56 @@ pub enum InsertPosition<T> {
106108 After ( T ) ,
107109}
108110
111+ type FxIndexMap < K , V > = IndexMap < K , V , BuildHasherDefault < rustc_hash:: FxHasher > > ;
112+
109113pub struct TreeDiff {
110114 replacements : FxHashMap < SyntaxElement , SyntaxElement > ,
115+ deletions : Vec < SyntaxElement > ,
116+ // the vec as well as the indexmap are both here to preserve order
117+ insertions : FxIndexMap < SyntaxElement , Vec < SyntaxElement > > ,
111118}
112119
113120impl TreeDiff {
114121 pub fn into_text_edit ( & self , builder : & mut TextEditBuilder ) {
122+ for ( anchor, to) in self . insertions . iter ( ) {
123+ to. iter ( ) . for_each ( |to| builder. insert ( anchor. text_range ( ) . end ( ) , to. to_string ( ) ) ) ;
124+ }
115125 for ( from, to) in self . replacements . iter ( ) {
116126 builder. replace ( from. text_range ( ) , to. to_string ( ) )
117127 }
128+ for text_range in self . deletions . iter ( ) . map ( SyntaxElement :: text_range) {
129+ builder. delete ( text_range) ;
130+ }
118131 }
119132
120133 pub fn is_empty ( & self ) -> bool {
121- self . replacements . is_empty ( )
134+ self . replacements . is_empty ( ) && self . deletions . is_empty ( ) && self . insertions . is_empty ( )
122135 }
123136}
124137
125138/// Finds minimal the diff, which, applied to `from`, will result in `to`.
126139///
127- /// Specifically, returns a map whose keys are descendants of `from` and values
128- /// are descendants of `to`, such that `replace_descendants( from, map) == to`.
140+ /// Specifically, returns a structure that consists of a replacements, insertions and deletions
141+ /// such that applying this map on ` from` will result in ` to`.
129142///
130- /// A trivial solution is a singleton map `{ from: to }`, but this function
131- /// tries to find a more fine-grained diff.
143+ /// This function tries to find a fine-grained diff.
132144pub fn diff ( from : & SyntaxNode , to : & SyntaxNode ) -> TreeDiff {
133- let mut buf = FxHashMap :: default ( ) ;
145+ let mut diff = TreeDiff {
146+ replacements : FxHashMap :: default ( ) ,
147+ insertions : FxIndexMap :: default ( ) ,
148+ deletions : Vec :: new ( ) ,
149+ } ;
150+ let ( from, to) = ( from. clone ( ) . into ( ) , to. clone ( ) . into ( ) ) ;
151+
134152 // FIXME: this is both horrible inefficient and gives larger than
135153 // necessary diff. I bet there's a cool algorithm to diff trees properly.
136- go ( & mut buf, from. clone ( ) . into ( ) , to. clone ( ) . into ( ) ) ;
137- return TreeDiff { replacements : buf } ;
154+ if !syntax_element_eq ( & from, & to) {
155+ go ( & mut diff, from, to) ;
156+ }
157+ return diff;
138158
139- fn go (
140- buf : & mut FxHashMap < SyntaxElement , SyntaxElement > ,
141- lhs : SyntaxElement ,
142- rhs : SyntaxElement ,
143- ) {
144- if lhs. kind ( ) == rhs. kind ( )
159+ fn syntax_element_eq ( lhs : & SyntaxElement , rhs : & SyntaxElement ) -> bool {
160+ lhs. kind ( ) == rhs. kind ( )
145161 && lhs. text_range ( ) . len ( ) == rhs. text_range ( ) . len ( )
146162 && match ( & lhs, & rhs) {
147163 ( NodeOrToken :: Node ( lhs) , NodeOrToken :: Node ( rhs) ) => {
@@ -150,18 +166,43 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
150166 ( NodeOrToken :: Token ( lhs) , NodeOrToken :: Token ( rhs) ) => lhs. text ( ) == rhs. text ( ) ,
151167 _ => false ,
152168 }
153- {
154- return ;
155- }
156- if let ( Some ( lhs) , Some ( rhs) ) = ( lhs. as_node ( ) , rhs. as_node ( ) ) {
157- if lhs. children_with_tokens ( ) . count ( ) == rhs. children_with_tokens ( ) . count ( ) {
158- for ( lhs, rhs) in lhs. children_with_tokens ( ) . zip ( rhs. children_with_tokens ( ) ) {
159- go ( buf, lhs, rhs)
160- }
169+ }
170+
171+ fn go ( diff : & mut TreeDiff , lhs : SyntaxElement , rhs : SyntaxElement ) {
172+ let ( lhs, rhs) = match lhs. as_node ( ) . zip ( rhs. as_node ( ) ) {
173+ Some ( ( lhs, rhs) ) => ( lhs, rhs) ,
174+ _ => {
175+ diff. replacements . insert ( lhs, rhs) ;
161176 return ;
162177 }
178+ } ;
179+
180+ let mut rhs_children = rhs. children_with_tokens ( ) ;
181+ let mut lhs_children = lhs. children_with_tokens ( ) ;
182+ let mut last_lhs = None ;
183+ loop {
184+ let lhs_child = lhs_children. next ( ) ;
185+ match ( lhs_child. clone ( ) , rhs_children. next ( ) ) {
186+ ( None , None ) => break ,
187+ ( None , Some ( element) ) => match last_lhs. clone ( ) {
188+ Some ( prev) => {
189+ diff. insertions . entry ( prev) . or_insert_with ( Vec :: new) . push ( element) ;
190+ }
191+ // first iteration, this means we got no anchor element to insert after
192+ // therefor replace the parent node instead
193+ None => {
194+ diff. replacements . insert ( lhs. clone ( ) . into ( ) , rhs. clone ( ) . into ( ) ) ;
195+ break ;
196+ }
197+ } ,
198+ ( Some ( element) , None ) => {
199+ diff. deletions . push ( element) ;
200+ }
201+ ( Some ( ref lhs_ele) , Some ( ref rhs_ele) ) if syntax_element_eq ( lhs_ele, rhs_ele) => { }
202+ ( Some ( lhs_ele) , Some ( rhs_ele) ) => go ( diff, lhs_ele, rhs_ele) ,
203+ }
204+ last_lhs = lhs_child. or ( last_lhs) ;
163205 }
164- buf. insert ( lhs, rhs) ;
165206 }
166207}
167208
0 commit comments