mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-25 03:08:42 +00:00
Merge "Make translateRange() translate across wrapping transactions correctly"
This commit is contained in:
commit
0d8bc60cc6
|
@ -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 );
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)' );
|
||||
}
|
||||
} );
|
||||
|
||||
|
|
Loading…
Reference in a new issue