Divec has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/315657

Change subject: ve.sparseSplice: Splice one array into another, replicating any 
holes
......................................................................

ve.sparseSplice: Splice one array into another, replicating any holes

Change-Id: I6a06cf90d7036c8bdadadd74c4a667b36e54d72b
---
M src/ve.utils.js
M tests/ve.test.js
2 files changed, 123 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/57/315657/1

diff --git a/src/ve.utils.js b/src/ve.utils.js
index 013d85a..4f18c13 100644
--- a/src/ve.utils.js
+++ b/src/ve.utils.js
@@ -224,7 +224,7 @@
  * Includes a replacement for broken implementations of 
Array.prototype.splice().
  *
  * @param {Array|ve.dm.BranchNode} arr Target object (must have `splice` 
method, object will be modified)
- * @param {number} offset Offset in arr to splice at. This may NOT be 
negative, unlike the
+ * @param {number} offset Offset in arr to splice at. This MUST NOT be 
negative, unlike the
  *  'index' parameter in Array#splice.
  * @param {number} remove Number of elements to remove at the offset. May be 
zero
  * @param {Array} data Array of items to insert at the offset. Must be 
non-empty if remove=0
@@ -287,6 +287,56 @@
 };
 
 /**
+ * Splice one array into another, replicating any holes
+ *
+ * Similar to arr.splice.apply( arr, [ offset, remove ].concat( data ) ), 
except
+ * holes in data are copied remain holes in arr. Optimized for length changes 
that are
+ * negative, zero, or fairly small positive.
+ *
+ * @param {Array} arr Array to modify
+ * @param {number} offset Offset in arr to splice at. This MUST NOT be 
negative, unlike the
+ *  'index' parameter in Array#splice.
+ * @param {number} remove Number of elements to remove at the offset. May be 
zero
+ * @param {Array} data Array of items to insert at the offset
+ * @return {Array} Array of items removed, with holes preserved
+ */
+ve.sparseSplice = function ( arr, offset, remove, data ) {
+       var i,
+               removed = [],
+               endOffset = offset + remove,
+               diff = data.length - remove;
+       if ( data === arr ) {
+               // Pathological case: arr and data are reference-identical
+               data = data.slice();
+       }
+       // Remove content without adjusting length
+       arr.slice( offset, endOffset ).forEach( function ( item, i ) {
+               removed[ i ] = item;
+               delete arr[ offset + i ];
+       } );
+       // Adjust length
+       if ( diff > 0 ) {
+               // Grow with undefined values, then delete. (This is optimised 
for diff
+               // comparatively small: otherwise, it would sometimes be 
quicker to relocate
+               // each element of arr that lies above offset).
+               ve.batchSplice( arr, endOffset, 0, new Array( diff ) );
+               for ( i = endOffset + diff - 1; i >= endOffset; i-- ) {
+                       delete arr[ i ];
+               }
+       } else if ( diff < 0 ) {
+               // Shrink
+               arr.splice( offset, -diff );
+       }
+       // Insert new content
+       data.forEach( function ( item, i ) {
+               arr[ offset + i ] = item;
+       } );
+       // Set removed.length in case there are holes at the end
+       removed.length = remove;
+       return removed;
+};
+
+/**
  * Insert one array into another.
  *
  * Shortcut for `ve.batchSplice( arr, offset, 0, src )`.
diff --git a/tests/ve.test.js b/tests/ve.test.js
index 8555a38..3997b5c 100644
--- a/tests/ve.test.js
+++ b/tests/ve.test.js
@@ -266,6 +266,78 @@
        );
 } );
 
+QUnit.test( 'sparseSplice', function ( assert ) {
+       var tests, i, len, test;
+       // Convert a sparse array of primitives to an array of strings, with '' 
for holes.
+       // This is needed because QUnit.equiv treats holes as equivalent to 
undefined.
+       function mapToString( flatArray ) {
+               var j, jLen,
+                       strings = [];
+               for ( j = 0, jLen = flatArray.length; j < jLen; j++ ) {
+                       strings.push( flatArray.hasOwnProperty( j ) ? String( 
flatArray[ j ] ) : '' );
+               }
+               return strings;
+       }
+       function runTest( arr, offset, remove, data, expectedReturn, 
expectedArray, msg ) {
+               var observedReturn,
+                       testArr = arr.slice();
+
+               observedReturn = ve.sparseSplice( testArr, offset, remove, data 
);
+               assert.deepEqual(
+                       mapToString( observedReturn ),
+                       mapToString( expectedReturn ),
+                       msg + ': return'
+               );
+               assert.deepEqual(
+                       mapToString( testArr ),
+                       mapToString( expectedArray ),
+                       msg + ': modification'
+               );
+       }
+       tests = [
+               /*jshint elision:true */
+               // jscs:disable disallowTrailingComma
+               // jscs:disable disallowSpaceBeforeBinaryOperators
+               // arr, offset, remove, data, expectedReturn, expectedArray, msg
+               [ [], 0, 0, [ 1, , 3 ], [], [ 1, , 3 ], 'insert empty, leading 
hole' ],
+               [ [], 0, 0, [ , 3 ], [], [ , 3 ], 'insert empty, middle hole' ],
+               // Note: the first trailing comma does not create a hole
+               [ [], 0, 0, [ 1, , ], [], [ 1, , ], 'insert empty, trailing 
hole' ],
+               [ [ 4, , 5 ], 0, 0, [ 1, , 3 ], [], [ 1, , 3, 4, , 5 ], 'insert 
start' ],
+               [ [ 0, , 4 ], 1, 0, [ 1, , 3 ], [], [ 0, 1, , 3, , 4 ], 'insert 
mid' ],
+               [ [ 0, , 4 ], 1, 0, [ 1, , 3 ], [], [ 0, 1, , 3, , 4 ], 'insert 
end' ],
+
+               [ [ 4, , 5, , 6 ], 0, 4, [ 1, , 3 ], [ 4, , 5, , ], [ 1, , 3, 6 
], 'diff<0 start' ],
+               [ [ 4, , , 5, , 6 ], 1, 4, [ 1, , 3 ], [ , , 5, , ], [ 4, 1, , 
3, 6 ], 'diff<0 mid' ],
+               [ [ 4, , 5, , 6 ], 1, 4, [ 1, , 3 ], [ , 5, , 6 ], [ 4, 1, , 3 
], 'diff<0 end' ],
+
+               [ [ 4, , 5, , 6 ], 0, 2, [ 1, , 3 ], [ 4, , ], [ 1, , 3, 5, , 6 
], 'diff>0 start' ],
+               [ [ 4, , 5, , 6 ], 1, 2, [ 1, , 3 ], [ , 5 ], [ 4, 1, , 3, , 6 
], 'diff>0 mid' ],
+               [ [ 4, , 5, , 6 ], 3, 2, [ 1, , 3 ], [ , 6 ], [ 4, , 5, 1, , 3 
], 'diff>0 end' ],
+
+               [ [ 4, , 5, , 6 ], 0, 3, [ 1, , 3 ], [ 4, , 5 ], [ 1, , 3, , 6 
], 'diff=0 start' ],
+               [ [ 4, , 5, , 6 ], 1, 3, [ 1, , 3 ], [ , 5, , ], [ 4, 1, , 3, 6 
], 'diff=0 mid' ],
+               [ [ 4, , 5, , 6 ], 2, 3, [ 1, , 3 ], [ 5, , 6 ], [ 4, , 1, , 3 
], 'diff=0 end' ]
+               // jscs:enable disallowSpaceBeforeBinaryOperators
+               // jscs:enable disallowTrailingComma
+               /*jshint elision:false */
+       ];
+       QUnit.expect( 2 * tests.length + 1 );
+       assert.notDeepEqual(
+               /*jshint elision:true */
+               // jscs:disable disallowTrailingComma
+               mapToString( [ 1, , ] ),
+               // jscs:enable disallowTrailingComma
+               /*jshint elision:false */
+               mapToString( [ 1, undefined ] ),
+               'holes look different to undefined'
+       );
+       for ( i = 0, len = tests.length; i < len; i++ ) {
+               test = tests[ i ];
+               runTest.apply( null, test );
+       }
+} );
+
 QUnit.test( 'batchSplice', function ( assert ) {
        var spliceWasSupported = ve.supportsSplice;
 

-- 
To view, visit https://gerrit.wikimedia.org/r/315657
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6a06cf90d7036c8bdadadd74c4a667b36e54d72b
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <da...@troi.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to