Merge "Make translateRange() translate across wrapping transactions correctly"

This commit is contained in:
jenkins-bot 2013-02-25 19:32:34 +00:00 committed by Gerrit Code Review
commit 0d8bc60cc6
2 changed files with 83 additions and 15 deletions

View file

@ -684,9 +684,12 @@ ve.dm.Transaction.prototype.toggleApplied = function () {
*
* @method
* @param {number} offset Offset in the linear model before the transaction has been processed
* @param {boolean} [reversed] Reverse the translation, i.e. translate based on a rollback
* @param {boolean} [excludeInsertion] Map the offset immediately before an insertion to
* right before the insertion rather than right after
* @returns {number} Translated offset, as it will be after processing transaction
*/
ve.dm.Transaction.prototype.translateOffset = function ( offset, reversed ) {
ve.dm.Transaction.prototype.translateOffset = function ( offset, reversed, excludeInsertion ) {
var i, op, insertLength, removeLength, prevAdjustment, cursor = 0, adjustment = 0;
for ( i = 0; i < this.operations.length; i++ ) {
op = this.operations[i];
@ -696,8 +699,14 @@ ve.dm.Transaction.prototype.translateOffset = function ( offset, reversed ) {
prevAdjustment = adjustment;
adjustment += insertLength - removeLength;
if ( offset === cursor + removeLength ) {
// Offset points to right after the removal, translate it
return offset + adjustment;
// Offset points to right after the removal or right before the insertion
if ( excludeInsertion && insertLength > removeLength ) {
// Translate it to before the insertion
return offset + adjustment - insertLength + removeLength;
} else {
// Translate it to after the removal/insertion
return offset + adjustment;
}
} else if ( offset === cursor ) {
// The offset points to right before the removal or replacement
if ( insertLength === 0 ) {
@ -737,7 +746,14 @@ ve.dm.Transaction.prototype.translateOffset = function ( offset, reversed ) {
* @returns {ve.Range} Translated range, as it will be after processing transaction
*/
ve.dm.Transaction.prototype.translateRange = function ( range, reversed ) {
return new ve.Range( this.translateOffset( range.from, reversed ), this.translateOffset( range.to, reversed ) );
var start = this.translateOffset( range.start, reversed, false ),
end = this.translateOffset( range.end, reversed, true );
if ( range.start <= range.end && start > end ) {
// translateOffset() has mapped end to jump over start
// Disable the excludeInsertion behavior in this case
end = this.translateOffset( range.end, reversed, false );
}
return range.isBackwards() ? new ve.Range( end, start ) : new ve.Range( start, end );
};
/**

View file

@ -774,7 +774,7 @@ QUnit.test( 'newFromWrap', 8, function ( assert ) {
} );
QUnit.test( 'translateOffset', function ( assert ) {
var tx, mapping, offset;
var tx, mapping, offset, expected;
// Populate a transaction with bogus data
tx = new ve.dm.Transaction();
tx.pushReplace( [], ['a','b','c'] );
@ -788,7 +788,7 @@ QUnit.test( 'translateOffset', function ( assert ) {
tx.pushReplace( [], ['n', 'o', 'p'] );
mapping = {
0: 3,
0: [0, 3],
1: 4,
2: 5,
3: 6,
@ -801,19 +801,21 @@ QUnit.test( 'translateOffset', function ( assert ) {
10: 9,
11: 10,
12: 11,
13: 16,
13: [12, 16],
14: 17,
15: 21,
15: [18, 21],
16: 22
};
QUnit.expect( 17 );
QUnit.expect( 2*17 );
for ( offset in mapping ) {
assert.strictEqual( tx.translateOffset( Number( offset ) ), mapping[offset] );
expected = ve.isArray( mapping[offset] ) ? mapping[offset] : [ mapping[offset], mapping[offset] ];
assert.strictEqual( tx.translateOffset( Number( offset ) ), expected[1], offset );
assert.strictEqual( tx.translateOffset( Number( offset ), false, true ), expected[0], offset + ' (excludeInsertion)' );
}
} );
QUnit.test( 'translateOffsetReversed', function ( assert ) {
var tx, mapping, offset;
var tx, mapping, offset, expected;
// Populate a transaction with bogus data
tx = new ve.dm.Transaction();
tx.pushReplace( [], ['a','b','c'] );
@ -835,7 +837,7 @@ QUnit.test( 'translateOffsetReversed', function ( assert ) {
5: 2,
6: 3,
7: 4,
8: 9,
8: [5, 9],
9: 10,
10: 11,
11: 12,
@ -843,11 +845,61 @@ QUnit.test( 'translateOffsetReversed', function ( assert ) {
13: 13,
14: 13,
15: 13,
16: 13
16: 13,
17: 14,
18: 15,
19: 15,
20: 15,
21: 15,
22: 16
};
QUnit.expect( 17 );
QUnit.expect( 23*2 );
for ( offset in mapping ) {
assert.strictEqual( tx.translateOffset( Number( offset ), true ), mapping[offset] );
expected = ve.isArray( mapping[offset] ) ? mapping[offset] : [ mapping[offset], mapping[offset] ];
assert.strictEqual( tx.translateOffset( Number( offset ), true ), expected[1], offset );
assert.strictEqual( tx.translateOffset( Number( offset ), true, true ), expected[0], offset + ' (excludeInsertion)' );
}
} );
QUnit.test( 'translateRange', function ( assert ) {
var i, cases, tx = new ve.dm.Transaction();
tx.pushRetain( 55 );
tx.pushReplace( [], [ { 'type': 'list', 'attributes': { 'style': 'number' } } ] );
tx.pushReplace( [], [ { 'type': 'listItem' } ] );
tx.pushRetain( 3 );
tx.pushReplace( [], [ { 'type': '/listItem' } ] );
tx.pushReplace( [], [ { 'type': 'listItem' } ] );
tx.pushRetain( 3 );
tx.pushReplace( [], [ { 'type': '/listItem' } ] );
tx.pushReplace( [], [ { 'type': '/list' } ] );
cases = [
{
'before': new ve.Range( 55, 61 ),
'after': new ve.Range( 57, 65 ),
'msg': 'Wrapped range is translated to inner range'
},
{
'before': new ve.Range( 54, 62 ),
'after': new ve.Range( 54, 68 ),
'msg': 'Wrapped range plus one each side is translated to outer range plus one each side'
},
{
'before': new ve.Range( 54, 61 ),
'after': new ve.Range( 54, 65 ),
'msg': 'Wrapped range plus one on the left'
},
{
'before': new ve.Range( 55, 62 ),
'after': new ve.Range( 57, 68 ),
'msg': 'wrapped range plus one on the right'
}
];
QUnit.expect( cases.length * 2 );
for ( i = 0; i < cases.length; i++ ) {
assert.deepEqual( tx.translateRange( cases[i].before ), cases[i].after, cases[i].msg );
assert.deepEqual( tx.translateRange( cases[i].before.flip() ), cases[i].after.flip(), cases[i].msg + ' (reversed)' );
}
} );