mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
Port structural replace code from the old VE code
This makes TransactionProcessor work for regular replacements, as well as insertions and deletions of self-contained pieces of data. This does NOT yet work for inserting and deleting unbalanced data (splitting/merging nodes). I've tested this from the console for insertions and deletions and simple replacements, but I haven't tested wrappings. We should write a bunch of unit tests for this some time :) Change-Id: Ic2fd75d1cf2e127bc9ae58debce67576be2c912f
This commit is contained in:
parent
3cc22b00cb
commit
ca17ea0943
|
@ -232,6 +232,77 @@ ve.dm.TransactionProcessor.prototype.replace = function( op ) {
|
|||
this.cursor += replacement.length;
|
||||
} else {
|
||||
// Structural replacement
|
||||
// TODO implement
|
||||
// TODO generalize for insert/remove
|
||||
|
||||
// It's possible that multiple replace operations are needed before the
|
||||
// model is back in a consistent state. This loop applies the current
|
||||
// replace operation to the linear model, then keeps applying subsequent
|
||||
// operations until the model is consistent. We keep track of the changes
|
||||
// and queue a single rebuild after the loop finishes.
|
||||
var operation = op,
|
||||
removeLevel = 0,
|
||||
replaceLevel = 0,
|
||||
startOffset = this.cursor,
|
||||
adjustment = 0,
|
||||
i,
|
||||
type;
|
||||
|
||||
while ( true ) {
|
||||
if ( operation.type == 'replace' ) {
|
||||
var opRemove = this.reversed ? operation.replacement : operation.remove,
|
||||
opReplacement = this.reversed ? operation.remove : operation.replacement;
|
||||
// Update the linear model for this replacement
|
||||
ve.batchSplice( this.document.data, this.cursor, opRemove.length, opReplacement );
|
||||
this.cursor += opReplacement.length;
|
||||
adjustment += opReplacement.length - opRemove.length;
|
||||
|
||||
// Walk through the remove and replacement data
|
||||
// and keep track of the element depth change (level)
|
||||
// for each of these two separately. The model is
|
||||
// only consistent if both levels are zero.
|
||||
for ( i = 0; i < opRemove.length; i++ ) {
|
||||
type = opRemove[i].type;
|
||||
if ( type === undefined ) {
|
||||
// This is content, ignore
|
||||
} else if ( type.charAt( 0 ) === '/' ) {
|
||||
// Closing element
|
||||
removeLevel--;
|
||||
} else {
|
||||
// Opening element
|
||||
removeLevel++;
|
||||
}
|
||||
}
|
||||
for ( i = 0; i < opReplacement.length; i++ ) {
|
||||
type = opReplacement[i].type;
|
||||
if ( type === undefined ) {
|
||||
// This is content, ignore
|
||||
} else if ( type.charAt( 0 ) === '/' ) {
|
||||
// Closing element
|
||||
replaceLevel--;
|
||||
} else {
|
||||
// Opening element
|
||||
replaceLevel++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We know that other operations won't cause adjustments, so we
|
||||
// don't have to update adjustment
|
||||
this.executeOperation( operation );
|
||||
}
|
||||
|
||||
if ( removeLevel === 0 && replaceLevel === 0 ) {
|
||||
// The model is back in a consistent state, so we're done
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the next operation
|
||||
operation = this.nextOperation();
|
||||
if ( !operation ) {
|
||||
throw 'Unbalanced set of replace operations found';
|
||||
}
|
||||
}
|
||||
// Queue a rebuild for the replaced node
|
||||
this.synchronizer.pushRebuild( new ve.Range( startOffset, this.cursor - adjustment ),
|
||||
new ve.Range( startOffset, this.cursor ) );
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue