@@ -13,13 +13,27 @@ cfg_if! {
1313}
1414
1515/// This struct represents a fragment of the Virtual DOM tree.
16- #[ derive( Clone , Debug , PartialEq , Default ) ]
16+ #[ derive( Clone , Debug , PartialEq ) ]
1717pub struct VList {
1818 /// 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+
2024 pub key : Option < Key > ,
2125}
2226
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+
2337impl Deref for VList {
2438 type Target = Vec < VNode > ;
2539
@@ -30,10 +44,13 @@ impl Deref for VList {
3044
3145impl DerefMut for VList {
3246 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+
3351 & mut self . children
3452 }
3553}
36-
3754/// Log an operation during tests for debugging purposes
3855/// Set RUSTFLAGS="--cfg verbose_tests" environment variable to activate.
3956macro_rules! test_log {
@@ -63,17 +80,37 @@ impl VList {
6380
6481 /// Creates a new `VList` instance with children.
6582 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+ }
6788 }
6889
6990 /// Add `VNode` child.
7091 pub fn add_child ( & mut self , child : VNode ) {
92+ if self . fully_keyed && !child. has_key ( ) {
93+ self . fully_keyed = false ;
94+ }
7195 self . children . push ( child) ;
7296 }
7397
7498 /// Add multiple `VNode` children.
7599 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 ( ) ) ;
77114 }
78115
79116 /// Diff and patch unkeyed child lists
@@ -132,10 +169,18 @@ impl VList {
132169 parent : & Element ,
133170 mut next_sibling : NodeRef ,
134171 lefts : & mut [ VNode ] ,
135- lefts_keys : & [ Key ] ,
136172 mut rights : Vec < VNode > ,
137- rights_keys : & [ Key ] ,
138173 ) -> 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+
139184 /// Find the first differing key in 2 iterators
140185 fn diff_i < ' a , ' b > (
141186 a : impl Iterator < Item = & ' a Key > ,
@@ -229,59 +274,6 @@ impl VList {
229274
230275 next_sibling
231276 }
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- }
285277}
286278
287279impl VDiff for VList {
@@ -311,28 +303,32 @@ impl VDiff for VList {
311303 // Without a placeholder the next element becomes first
312304 // and corrupts the order of rendering
313305 // 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 ( ) ) ;
316307 }
317308
318309 let lefts = & mut self . children ;
319- let rights = match ancestor {
310+ let ( rights, rights_fully_keyed ) = match ancestor {
320311 // If the ancestor is also a VList, then the "right" list is the previously
321312 // rendered items.
322- Some ( VNode :: VList ( v) ) => v. children ,
313+ Some ( VNode :: VList ( v) ) => ( v. children , v. fully_keyed ) ,
314+
323315 // 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 ) ,
326323 } ;
327324 test_log ! ( "lefts: {:?}" , lefts) ;
328325 test_log ! ( "rights: {:?}" , rights) ;
329326
330327 #[ 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)
336332 } ;
337333 test_log ! ( "result: {:?}" , lefts) ;
338334 first
0 commit comments