@@ -13,13 +13,27 @@ cfg_if! {
13
13
}
14
14
15
15
/// This struct represents a fragment of the Virtual DOM tree.
16
- #[ derive( Clone , Debug , PartialEq , Default ) ]
16
+ #[ derive( Clone , Debug , PartialEq ) ]
17
17
pub struct VList {
18
18
/// The list of children nodes.
19
- pub children : Vec < VNode > ,
19
+ children : Vec < VNode > ,
20
+
21
+ // All Nodes in the VList have keys
22
+ fully_keyed : bool ,
23
+
20
24
pub key : Option < Key > ,
21
25
}
22
26
27
+ impl Default for VList {
28
+ fn default ( ) -> Self {
29
+ Self {
30
+ children : Default :: default ( ) ,
31
+ key : None ,
32
+ fully_keyed : true ,
33
+ }
34
+ }
35
+ }
36
+
23
37
impl Deref for VList {
24
38
type Target = Vec < VNode > ;
25
39
@@ -30,10 +44,13 @@ impl Deref for VList {
30
44
31
45
impl DerefMut for VList {
32
46
fn deref_mut ( & mut self ) -> & mut Self :: Target {
47
+ // Caller might change the keys of the VList or add unkeyed children.
48
+ // Defensively assume they will.
49
+ self . fully_keyed = false ;
50
+
33
51
& mut self . children
34
52
}
35
53
}
36
-
37
54
/// Log an operation during tests for debugging purposes
38
55
/// Set RUSTFLAGS="--cfg verbose_tests" environment variable to activate.
39
56
macro_rules! test_log {
@@ -63,17 +80,37 @@ impl VList {
63
80
64
81
/// Creates a new `VList` instance with children.
65
82
pub fn new_with_children ( children : Vec < VNode > , key : Option < Key > ) -> Self {
66
- VList { children, key }
83
+ VList {
84
+ fully_keyed : children. iter ( ) . all ( |ch| ch. has_key ( ) ) ,
85
+ children,
86
+ key,
87
+ }
67
88
}
68
89
69
90
/// Add `VNode` child.
70
91
pub fn add_child ( & mut self , child : VNode ) {
92
+ if self . fully_keyed && !child. has_key ( ) {
93
+ self . fully_keyed = false ;
94
+ }
71
95
self . children . push ( child) ;
72
96
}
73
97
74
98
/// Add multiple `VNode` children.
75
99
pub fn add_children ( & mut self , children : impl IntoIterator < Item = VNode > ) {
76
- self . children . extend ( children) ;
100
+ let it = children. into_iter ( ) ;
101
+ let bound = it. size_hint ( ) ;
102
+ self . children . reserve ( bound. 1 . unwrap_or ( bound. 0 ) ) ;
103
+ for ch in it {
104
+ self . add_child ( ch) ;
105
+ }
106
+ }
107
+
108
+ /// Recheck, if the all the children have keys.
109
+ ///
110
+ /// Run this, after modifying the child list that contained only keyed children prior to the
111
+ /// mutable dereference.
112
+ pub fn recheck_fully_keyed ( & mut self ) {
113
+ self . fully_keyed = self . children . iter ( ) . all ( |ch| ch. has_key ( ) ) ;
77
114
}
78
115
79
116
/// Diff and patch unkeyed child lists
@@ -132,10 +169,18 @@ impl VList {
132
169
parent : & Element ,
133
170
mut next_sibling : NodeRef ,
134
171
lefts : & mut [ VNode ] ,
135
- lefts_keys : & [ Key ] ,
136
172
mut rights : Vec < VNode > ,
137
- rights_keys : & [ Key ] ,
138
173
) -> NodeRef {
174
+ macro_rules! map_keys {
175
+ ( $src: expr) => {
176
+ $src. iter( )
177
+ . map( |v| v. key( ) . expect( "unkeyed child in fully keyed list" ) )
178
+ . collect:: <Vec <Key >>( )
179
+ } ;
180
+ }
181
+ let lefts_keys = map_keys ! ( lefts) ;
182
+ let rights_keys = map_keys ! ( rights) ;
183
+
139
184
/// Find the first differing key in 2 iterators
140
185
fn diff_i < ' a , ' b > (
141
186
a : impl Iterator < Item = & ' a Key > ,
@@ -229,59 +274,6 @@ impl VList {
229
274
230
275
next_sibling
231
276
}
232
-
233
- /// Diff and patch child lists, if the are fully keyed.
234
- /// Returns Err(rights, next_sibling) back, if diff and patch not performed.
235
- fn try_apply_keyed (
236
- parent_scope : & AnyScope ,
237
- parent : & Element ,
238
- next_sibling : NodeRef ,
239
- lefts : & mut [ VNode ] ,
240
- rights : Vec < VNode > ,
241
- ) -> Result < NodeRef , ( Vec < VNode > , NodeRef ) > {
242
- macro_rules! fail {
243
- ( ) => { {
244
- return Err ( ( rights, next_sibling) ) ;
245
- } } ;
246
- }
247
-
248
- /// First perform a cheap check on the first nodes to avoid allocations
249
- macro_rules! no_key {
250
- ( $list: expr) => {
251
- $list. get( 0 ) . map( |n| n. key( ) . is_none( ) ) . unwrap_or( false )
252
- } ;
253
- }
254
- if no_key ! ( lefts) || no_key ! ( rights) {
255
- fail ! ( ) ;
256
- }
257
-
258
- // Build key vectors ahead of time to prevent heavy branching in keyed diffing, in case
259
- // the child lists are not fully keyed, and to memoize key lookup
260
- macro_rules! map_keys {
261
- ( $src: expr) => {
262
- match $src
263
- . iter( )
264
- . map( |v| v. key( ) . ok_or( ( ) ) )
265
- . collect:: <Result <Vec <Key >, ( ) >>( )
266
- {
267
- Ok ( vec) => vec,
268
- Err ( _) => fail!( ) ,
269
- }
270
- } ;
271
- }
272
- let lefts_keys = map_keys ! ( lefts) ;
273
- let rights_keys = map_keys ! ( rights) ;
274
-
275
- Ok ( Self :: apply_keyed (
276
- parent_scope,
277
- parent,
278
- next_sibling,
279
- lefts,
280
- & lefts_keys,
281
- rights,
282
- & rights_keys,
283
- ) )
284
- }
285
277
}
286
278
287
279
impl VDiff for VList {
@@ -311,28 +303,32 @@ impl VDiff for VList {
311
303
// Without a placeholder the next element becomes first
312
304
// and corrupts the order of rendering
313
305
// We use empty text element to stake out a place
314
- let placeholder = VText :: new ( "" ) ;
315
- self . children . push ( placeholder. into ( ) ) ;
306
+ self . add_child ( VText :: new ( "" ) . into ( ) ) ;
316
307
}
317
308
318
309
let lefts = & mut self . children ;
319
- let rights = match ancestor {
310
+ let ( rights, rights_fully_keyed ) = match ancestor {
320
311
// If the ancestor is also a VList, then the "right" list is the previously
321
312
// rendered items.
322
- Some ( VNode :: VList ( v) ) => v. children ,
313
+ Some ( VNode :: VList ( v) ) => ( v. children , v. fully_keyed ) ,
314
+
323
315
// If the ancestor was not a VList, then the "right" list is a single node
324
- Some ( v) => vec ! [ v] ,
325
- _ => vec ! [ ] ,
316
+ Some ( v) => {
317
+ let has_key = v. has_key ( ) ;
318
+ ( vec ! [ v] , has_key)
319
+ }
320
+
321
+ // No unkeyed nodes in an empty VList
322
+ _ => ( vec ! [ ] , true ) ,
326
323
} ;
327
324
test_log ! ( "lefts: {:?}" , lefts) ;
328
325
test_log ! ( "rights: {:?}" , rights) ;
329
326
330
327
#[ allow( clippy:: let_and_return) ]
331
- let first = match Self :: try_apply_keyed ( parent_scope, parent, next_sibling, lefts, rights) {
332
- Ok ( n) => n,
333
- Err ( ( rights, next_sibling) ) => {
334
- Self :: apply_unkeyed ( parent_scope, parent, next_sibling, lefts, rights)
335
- }
328
+ let first = if self . fully_keyed && rights_fully_keyed {
329
+ Self :: apply_keyed ( parent_scope, parent, next_sibling, lefts, rights)
330
+ } else {
331
+ Self :: apply_unkeyed ( parent_scope, parent, next_sibling, lefts, rights)
336
332
} ;
337
333
test_log ! ( "result: {:?}" , lefts) ;
338
334
first
0 commit comments