@@ -5,6 +5,17 @@ use itertools::Itertools;
5
5
use crate :: * ;
6
6
use DisplayTreeKind :: * ;
7
7
8
+ /// How to compare two separated lists of different lengths.
9
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
10
+ pub enum CompareMode {
11
+ /// We consider the whole subtree to differ.
12
+ None ,
13
+ /// The first elements are diffed together until exhaustion.
14
+ Prefix ,
15
+ /// The last elements are diffed together until exhaustion.
16
+ Suffix ,
17
+ }
18
+
8
19
/// A structured representation of a string to be displayed. Used to compute structured diffs.
9
20
#[ derive( Clone , Copy ) ]
10
21
enum DisplayTreeKind < ' a > {
@@ -17,10 +28,8 @@ enum DisplayTreeKind<'a> {
17
28
sep : & ' a str ,
18
29
/// The children.
19
30
children : & ' a [ DisplayTree < ' a > ] ,
20
- /// If the lengths differ and this is `true`, the first elements are diffed together until
21
- /// exhaustion. If the lengths differ and this is `false`, we consider the whole subtree to
22
- /// differ.
23
- compare_common_prefix : bool ,
31
+ /// How to compare elements if the lengths differ.
32
+ compare_mode : CompareMode ,
24
33
} ,
25
34
}
26
35
@@ -119,11 +128,11 @@ impl<'a> DisplayTree<'a> {
119
128
Self :: leaf_noalloc ( a. alloc_str ( s) )
120
129
}
121
130
122
- fn mk_separated (
131
+ pub fn sep_by_compare_mode (
123
132
a : & ' a Arenas < ' a > ,
124
133
sep : & str ,
125
134
children : impl IntoIterator < Item : ToDisplayTree < ' a > > ,
126
- compare_common_prefix : bool ,
135
+ compare_mode : CompareMode ,
127
136
) -> Self {
128
137
let children = children
129
138
. into_iter ( )
@@ -132,7 +141,7 @@ impl<'a> DisplayTree<'a> {
132
141
Self :: new_from_kind ( Separated {
133
142
sep : a. alloc_str ( sep) ,
134
143
children : a. bump . alloc_slice_copy ( & children) ,
135
- compare_common_prefix ,
144
+ compare_mode ,
136
145
} )
137
146
}
138
147
@@ -141,15 +150,15 @@ impl<'a> DisplayTree<'a> {
141
150
sep : & str ,
142
151
children : impl IntoIterator < Item : ToDisplayTree < ' a > > ,
143
152
) -> Self {
144
- Self :: mk_separated ( a, sep, children, false )
153
+ Self :: sep_by_compare_mode ( a, sep, children, CompareMode :: None )
145
154
}
146
155
147
156
pub fn sep_by_compare_prefix (
148
157
a : & ' a Arenas < ' a > ,
149
158
sep : & str ,
150
159
children : impl IntoIterator < Item : ToDisplayTree < ' a > > ,
151
160
) -> Self {
152
- Self :: mk_separated ( a, sep, children, true )
161
+ Self :: sep_by_compare_mode ( a, sep, children, CompareMode :: Prefix )
153
162
}
154
163
155
164
/// Concatenates `self` and `x`, separated by `sep`.
@@ -235,26 +244,44 @@ impl<'a> DisplayTree<'a> {
235
244
Separated {
236
245
sep,
237
246
children : c1,
238
- compare_common_prefix : ccp1,
247
+ compare_mode : ccp1,
239
248
} ,
240
249
Separated {
241
250
sep : sep2,
242
251
children : c2,
243
- compare_common_prefix : ccp2 ,
252
+ compare_mode : _ , // we only look at one of tham
244
253
} ,
245
254
) if strip_markup ( sep) == strip_markup ( sep2)
246
- && ( c1. len ( ) == c2. len ( ) || ccp1 || ccp2 ) =>
255
+ && ( c1. len ( ) == c2. len ( ) || ccp1 != CompareMode :: None ) =>
247
256
{
257
+ use std:: iter:: repeat_n;
258
+ // Pad the two lists so they have the same length; pad at the start or end
259
+ // depending on how we want to match the lists up.
260
+ let len1 = c1. len ( ) ;
261
+ let len2 = c2. len ( ) ;
262
+ let len = std:: cmp:: max ( len1, len2) ;
263
+ let ( prefix1, prefix2, suffix1, suffix2) = match ccp1 {
264
+ CompareMode :: None => ( 0 , 0 , 0 , 0 ) ,
265
+ CompareMode :: Prefix => ( 0 , 0 , len - len1, len - len2) ,
266
+ CompareMode :: Suffix => ( len - len1, len - len2, 0 , 0 ) ,
267
+ } ;
268
+ let c1 = repeat_n ( None , prefix1)
269
+ . chain ( c1. iter ( ) . copied ( ) . map ( Some ) )
270
+ . chain ( repeat_n ( None , suffix1) ) ;
271
+ let c2 = repeat_n ( None , prefix2)
272
+ . chain ( c2. iter ( ) . copied ( ) . map ( Some ) )
273
+ . chain ( repeat_n ( None , suffix2) ) ;
274
+
248
275
let mut is_first = true ;
249
276
let mut any_diff = false ;
250
- for either_or_both in c1. iter ( ) . copied ( ) . zip_longest ( c2 . iter ( ) . copied ( ) ) {
251
- if !is_first && !either_or_both . is_right ( ) {
277
+ for ( l , r ) in c1. zip ( c2 ) {
278
+ if !is_first && !l . is_none ( ) {
252
279
write ! ( left, "{sep}" ) ?;
253
280
}
254
- if !is_first && !either_or_both . is_left ( ) {
281
+ if !is_first && !r . is_none ( ) {
255
282
write ! ( right, "{sep}" ) ?;
256
283
}
257
- let ( c1, c2) = either_or_both . or_default ( ) ;
284
+ let ( c1, c2) = ( l . unwrap_or_default ( ) , r . unwrap_or_default ( ) ) ;
258
285
any_diff |= c1. diff_display_inner ( & c2, left, right) ?;
259
286
is_first = false ;
260
287
}
0 commit comments