Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #499 from ckeditor/t/undo-2
Browse files Browse the repository at this point in the history
T/undo 2
  • Loading branch information
Piotr Jasiun authored Jun 24, 2016
2 parents 8a7aa83 + 5030ee8 commit f47a6f4
Show file tree
Hide file tree
Showing 20 changed files with 622 additions and 230 deletions.
3 changes: 2 additions & 1 deletion src/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ export default class DataController {
const modelRoot = this.model.getRoot( rootName );

this.model.enqueueChanges( () => {
this.model.batch()
// Initial batch should be ignored by features like undo, etc.
this.model.batch( 'transparent' )
.remove( ModelRange.createFromElement( modelRoot ) )
.insert( ModelPosition.createAt( modelRoot, 0 ), this.parse( data ) );
} );
Expand Down
38 changes: 31 additions & 7 deletions src/model/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,51 @@ export default class Batch {
* Creates Batch instance. Not recommended to use directly, use {@link engine.model.Document#batch} instead.
*
* @param {engine.model.Document} doc Document which this Batch changes.
* @param {'transparent'|'default'} [type='default'] Type of the batch.
*/
constructor( doc ) {
constructor( doc, type = 'default' ) {
/**
* Document which this Batch changes.
* Document which this batch changes.
*
* @member {engine.model.Document} engine.model.Batch#doc
* @readonly
* @member {engine.model.Document} engine.model.Batch#doc
*/
this.doc = doc;

/**
* Array of deltas which compose Batch.
* Array of deltas which compose this batch.
*
* @member {Array.<engine.model.delta.Delta>} engine.model.Batch#deltas
* @readonly
* @member {Array.<engine.model.delta.Delta>} engine.model.Batch#deltas
*/
this.deltas = [];

/**
* Type of the batch.
*
* Can be one of the following values:
* * `'default'` - all "normal" batches, most commonly used type.
* * `'transparent'` - batch that should be ignored by other features, i.e. initial batch or collaborative editing changes.
*
* @readonly
* @member {'transparent'|'default'} engine.model.Batch#type
*/
this.type = type;
}

/**
* Returns this batch base version, which is equal to the base version of first delta in the batch.
* If there are no deltas in the batch, it returns `null`.
*
* @readonly
* @type {Number|null}
*/
get baseVersion() {
return this.deltas.length > 0 ? this.deltas[ 0 ].baseVersion : null;
}

/**
* Adds delta to the Batch instance. All modification methods (insert, remove, split, etc.) use this method
* Adds delta to the batch instance. All modification methods (insert, remove, split, etc.) use this method
* to add created deltas.
*
* @param {engine.model.delta.Delta} delta Delta to add.
Expand All @@ -81,7 +105,7 @@ export default class Batch {
}

/**
* Function to register Batch methods. To make code scalable Batch do not have modification
* Function to register batch methods. To make code scalable Batch do not have modification
* methods built in. They can be registered using this method.
*
* This method checks if there is no naming collision and throws `batch-register-taken` if the method name
Expand Down
11 changes: 10 additions & 1 deletion src/model/delta/basic-transformations.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,19 @@ addTransformationCase( UnwrapDelta, SplitDelta, ( a, b, isStrong ) => {
// If incoming unwrap delta tries to unwrap node that got split we should unwrap the original node and the split copy.
// This can be achieved either by reverting split and applying unwrap to singular node, or creating additional unwrap delta.
if ( compareArrays( a.position.path, b.position.getParentPath() ) === 'SAME' ) {
return [
const transformed = [
b.getReversed(),
a.clone()
];

// It's a kind of magic-magic-magic-maaaaagiiic!
transformed[ 1 ].operations[ 1 ].targetPosition.path[ 0 ]++;
// But seriously, we have to fix RemoveOperation in the second delta because reversed UnwrapDelta creates
// MergeDelta which also has RemoveOperation. Those two operations cannot point to the same "holder" element
// in the graveyard, so we fix it by hand. This is the only case where it happens in "special" transformation
// cases, and it won't happen for "default" transformation apart of RemoveDelta, where it is okay.

return transformed;
}

return defaultTransform( a, b, isStrong );
Expand Down
14 changes: 10 additions & 4 deletions src/model/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ export default class Document {
/**
* Document's history.
*
* **Note:** Be aware that deltas applied to the stored deltas might be removed or changed.
*
* @readonly
* @member {engine.model.History} engine.model.Document#history
*/
this.history = new History();
this.history = new History( this );
}

/**
Expand Down Expand Up @@ -159,7 +161,10 @@ export default class Document {

this.version++;

this.history.addOperation( operation );
if ( operation.delta ) {
// Right now I can't imagine operations without deltas, but let's be safe.
this.history.addDelta( operation.delta );
}

const batch = operation.delta && operation.delta.batch;

Expand All @@ -172,10 +177,11 @@ export default class Document {
/**
* Creates a {@link engine.model.Batch} instance which allows to change the document.
*
* @param {String} [type] Batch type. See {@link engine.model.Batch#type}.
* @returns {engine.model.Batch} Batch instance.
*/
batch() {
return new Batch( this );
batch( type ) {
return new Batch( this, type );
}

/**
Expand Down
Loading

0 comments on commit f47a6f4

Please sign in to comment.