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 #902 from ckeditor/t/828
Browse files Browse the repository at this point in the history
Feature: Introduced `dev-utils.DeltaReplayer`. Introduced new logging methods in `dev-utils.enableEngineDebug`. Closes #828.
  • Loading branch information
scofalik authored Apr 4, 2017
2 parents 0aec182 + dd3015b commit eb855d9
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 2 deletions.
135 changes: 135 additions & 0 deletions src/dev-utils/deltareplayer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module engine/dev-utils/deltareplayer
*/

/* global setTimeout, console */

import DeltaFactory from '../model/delta/deltafactory';

/**
* DeltaReplayer is a dev-tool created for easily replaying operations on the document from stringified deltas.
*/
export default class DeltaReplayer {
/**
* @param {module:engine/model/document~Document} document Document to reply deltas on.
* @param {String} logSeparator Separator between deltas.
* @param {String} stringifiedDeltas Deltas to replay.
*/
constructor( document, logSeparator, stringifiedDeltas ) {
this._document = document;
this._logSeparator = logSeparator;
this.setStringifiedDeltas( stringifiedDeltas );
}

/**
* Parses given string containing stringified deltas and sets parsed deltas as deltas to reply.
*
* @param {String} stringifiedDeltas Stringified deltas to replay.
*/
setStringifiedDeltas( stringifiedDeltas ) {
if ( stringifiedDeltas === '' ) {
this._deltasToReplay = [];

return;
}

this._deltasToReplay = stringifiedDeltas
.split( this._logSeparator )
.map( stringifiedDelta => JSON.parse( stringifiedDelta ) );
}

/**
* Returns deltas to reply.
*
* @returns {Array.<module:engine/model/delta/delta~Delta>}
*/
getDeltasToReplay() {
return this._deltasToReplay;
}

/**
* Applies all deltas with delay between actions.
*
* @param {Number} timeInterval Time between applying deltas.
* @returns {Promise}
*/
play( timeInterval = 1000 ) {
const deltaReplayer = this;

return new Promise( ( res ) => {
play();

function play() {
if ( deltaReplayer._deltasToReplay.length === 0 ) {
return res();
}

deltaReplayer.applyNextDelta().then( () => {
setTimeout( play, timeInterval );
}, res );
}
} );
}

/**
* Applies `numberOfDeltas` deltas, beginning after the last applied delta (or first delta, if no deltas were applied).
*
* @param {Number} numberOfDeltas Number of deltas to apply.
* @returns {Promise}
*/
applyDeltas( numberOfDeltas ) {
if ( numberOfDeltas <= 0 ) {
return;
}

return this.applyNextDelta()
.then( () => this.applyDeltas( numberOfDeltas - 1 ) )
.catch( err => console.warn( err ) );
}

/**
* Applies all deltas to replay at once.
*
* @returns {Promise}
*/
applyAllDeltas() {
return this.applyNextDelta()
.then( () => this.applyAllDeltas() )
.catch( () => {} );
}

/**
* Applies the next delta to replay.
*
* @returns {Promise}
*/
applyNextDelta() {
const document = this._document;

return new Promise( ( res, rej ) => {
document.enqueueChanges( () => {
const jsonDelta = this._deltasToReplay.shift();

if ( !jsonDelta ) {
return rej( new Error( 'No deltas to replay' ) );
}

const delta = DeltaFactory.fromJSON( jsonDelta, this._document );

const batch = document.batch();
batch.addDelta( delta );

for ( const operation of delta.operations ) {
document.applyOperation( operation );
}

res();
} );
} );
}
}
49 changes: 47 additions & 2 deletions src/dev-utils/enableenginedebug.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ import ViewDocumentFragment from '../view/documentfragment';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';

import DeltaReplayer from './deltareplayer';

const treeDump = Symbol( '_treeDump' );

// Maximum number of stored states of model and view document.
const maxTreeDumpLength = 20;

// Separator used to separate stringified deltas
const LOG_SEPARATOR = '\n----------------\n';

// Specified whether debug tools were already enabled.
let enabled = false;

Expand Down Expand Up @@ -96,14 +101,17 @@ let log = console.log;
* @param {Function} [logger] Function used to log messages. By default messages are logged to console.
* @returns {module:engine/dev-utils/enableenginedebug~DebugPlugin} Plugin to be loaded in the editor.
*/
export default function enableEngineDebug( logger = console.log ) {
log = logger;
export default function enableEngineDebug( logger ) {
if ( logger ) {
log = logger;
}

if ( !enabled ) {
enabled = true;

enableLoggingTools();
enableDocumentTools();
enableReplayerTools();
}

return DebugPlugin;
Expand Down Expand Up @@ -448,12 +456,49 @@ function enableLoggingTools() {
};
}

function enableReplayerTools() {
const _modelDocumentApplyOperation = ModelDocument.prototype.applyOperation;

ModelDocument.prototype.applyOperation = function( operation ) {
if ( !this._lastDelta ) {
this._appliedDeltas = [];
} else if ( this._lastDelta !== operation.delta ) {
this._appliedDeltas.push( this._lastDelta.toJSON() );
}

this._lastDelta = operation.delta;

_modelDocumentApplyOperation.call( this, operation );
};

ModelDocument.prototype.getAppliedDeltas = function() {
// No deltas has been applied yet, return empty string.
if ( !this._lastDelta ) {
return '';
}

const appliedDeltas = this._appliedDeltas.concat( this._lastDelta.toJSON() );

return appliedDeltas.map( JSON.stringify ).join( LOG_SEPARATOR );
};

ModelDocument.prototype.createReplayer = function( stringifiedDeltas ) {
return new DeltaReplayer( this, LOG_SEPARATOR, stringifiedDeltas );
};
}

function enableDocumentTools() {
const _modelDocumentApplyOperation = ModelDocument.prototype.applyOperation;

ModelDocument.prototype.applyOperation = function( operation ) {
log( 'Applying ' + operation );

if ( !this._operationLogs ) {
this._operationLogs = [];
}

this._operationLogs.push( JSON.stringify( operation.toJSON() ) );

_modelDocumentApplyOperation.call( this, operation );
};

Expand Down
Loading

0 comments on commit eb855d9

Please sign in to comment.