Skip to content

Commit

Permalink
Compose implementation with simple tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jhchen committed Sep 2, 2014
1 parent 4770d05 commit 9bdc9b2
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 29 deletions.
3 changes: 2 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module.exports = function(grunt) {
var tests = [
'test/is.js',
'test/op.js',
'test/delta/builder.js'
'test/delta/builder.js',
'test/delta/compose.js'
];

grunt.registerTask('coverage', function() {
Expand Down
81 changes: 55 additions & 26 deletions lib/delta.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
var is = require('./is');
var op = require('./op');


var Delta = function(ops) {
this.ops = [];
if (is.array(ops)) {
ops.forEach((function(op) {
if (is.string(op)) {
this.insert(op);
} else if (is.number(op)) {
if (op > 0) {
this.retain(op);
ops.forEach((function(nextOp) {
if (is.string(nextOp)) {
this.insert(nextOp);
} else if (is.number(nextOp)) {
if (nextOp > 0) {
this.retain(nextOp);
} else {
this.delete(op);
this.delete(nextOp);
}
} else {
this._push(op);
this._push(nextOp);
}
}).bind(this));
} else if (is.object(ops) && is.array(ops.ops)) {
Expand All @@ -25,18 +26,18 @@ var Delta = function(ops) {


Delta.prototype.insert = function(text, formats) {
var op = {};
var newOp = {};
if (is.object(text) && !is.object(formats)) {
formats = text;
text = null;
}
if (is.string(text)) {
op.insert = text;
newOp.insert = text;
}
if (is.object(formats)) {
op.formats = formats;
newOp.formats = formats;
}
return this._push(op);
return this._push(newOp);
};

Delta.prototype.delete = function(length) {
Expand All @@ -46,37 +47,65 @@ Delta.prototype.delete = function(length) {

Delta.prototype.retain = function(length, formats) {
if (length === 0) return this;
var op = { retain: length };
if (is.object(formats)) op.formats = formats;
return this._push(op);
var newOp = { retain: length };
if (is.object(formats)) newOp.formats = formats;
return this._push(newOp);
};


Delta.prototype._push = function(op) {
Delta.prototype._push = function(newOp) {
var lastOp = this.ops[this.ops.length - 1];
if (is.object(lastOp)) {
if (is.number(op.delete) && is.number(lastOp.delete)) {
lastOp.delete += op.delete;
if (is.number(newOp.delete) && is.number(lastOp.delete)) {
lastOp.delete += newOp.delete;
return this;
} else {
if (is.equal(op.formats, lastOp.formats)) {
if (is.string(op.insert) && is.string(lastOp.insert)) {
lastOp.insert += op.insert;
if (is.equal(newOp.formats, lastOp.formats)) {
if (is.string(newOp.insert) && is.string(lastOp.insert)) {
lastOp.insert += newOp.insert;
return this;
} else if (is.number(op.retain) && is.number(lastOp.retain)) {
lastOp.retain += op.retain;
} else if (is.number(newOp.retain) && is.number(lastOp.retain)) {
lastOp.retain += newOp.retain;
return this;
}
}
}
}
this.ops.push(op);
this.ops.push(newOp);
return this;
};


Delta.prototype.compose = function(other) {

var thisIter = op.iterator(this.ops);
var otherIter = op.iterator(other.ops);
this.ops = [];
while (thisIter.hasNext() || otherIter.hasNext()) {
if (otherIter.peekType() === 'insert') {
this._push(otherIter.next());
} else if (thisIter.peekType() === 'delete') {
this._push(thisIter.next());
} else {
var length = Math.min(thisIter.peekLength(), otherIter.peekLength());
var thisOp = thisIter.next(length);
var otherOp = otherIter.next(length);
if (is.number(otherOp.delete)) {
if (is.number(thisOp.retain) && otherOp.retain !== Infinity) {
this._push(otherOp);
}
} else {
// Other op must be a retain
var newOp = {};
if (is.number(thisOp.retain)) newOp.retain = thisOp.retain;
// Embed inserts do not have insert key so cannot check isInsert
if (is.string(thisOp.insert)) newOp.insert = thisOp.insert;
if (is.object(thisOp.formats) || is.object(otherOp.formats)) {
newOp.formats = op.composeFormats(thisOp.formats, otherOp.formats);
}
this._push(newOp);
}
}
}
return this;
};

Delta.prototype.transform = function(other, priority) {
Expand Down
2 changes: 1 addition & 1 deletion lib/op.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Iterator.prototype.next = function(length) {
return retOp;
}
} else {
return { retain: Infinity };
return { retain: length };
}
};

Expand Down
70 changes: 70 additions & 0 deletions test/delta/compose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
var Delta = require('../../lib/delta');
var expect = require('chai').expect;


describe('compose', function() {
describe('single ops', function() {
it('insert + insert', function() {
var a = new Delta().insert('A');
var b = new Delta().insert('B');
var expected = new Delta().insert('B').insert('A');
expect(a.compose(b)).to.deep.equal(expected);
});

it('insert + retain', function() {
var a = new Delta().insert('A');
var b = new Delta().retain(1, { bold: true, color: 'red' });
var expected = new Delta().insert('A', { bold: true, color: 'red' });
expect(a.compose(b)).to.deep.equal(expected);
});

it('insert + delete', function() {
var a = new Delta().insert('A');
var b = new Delta().delete(-1);
var expected = new Delta();
expect(a.compose(b)).to.deep.equal(expected);
});

it('delete + insert', function() {
var a = new Delta().delete(-1);
var b = new Delta().insert('B');
var expected = new Delta().insert('B').delete(-1);
expect(a.compose(b)).to.deep.equal(expected);
});

it('delete + retain', function() {
var a = new Delta().delete(-1);
var b = new Delta().retain(1, { bold: true, color: 'red' });
var expected = new Delta().delete(-1).retain(1, { bold: true, color: 'red' });
expect(a.compose(b)).to.deep.equal(expected);
});

it('delete + delete', function() {
var a = new Delta().delete(-1).retain(1); // Need end length to be 1 to apply another delete
var b = new Delta().delete(-1);
var expected = new Delta().delete(-1).delete(-1);
expect(a.compose(b)).to.deep.equal(expected);
});

it('retain + insert', function() {
var a = new Delta().retain(1, { color: 'blue' });
var b = new Delta().insert('B');
var expected = new Delta().insert('B').retain(1, { color: 'blue' });
expect(a.compose(b)).to.deep.equal(expected);
});

it('retain + retain', function() {
var a = new Delta().retain(1, { color: 'blue' });
var b = new Delta().retain(1, { bold: true, color: 'red' });
var expected = new Delta().retain(1, { bold: true, color: 'red' });
expect(a.compose(b)).to.deep.equal(expected);
});

it('retain + delete', function() {
var a = new Delta().retain(1, { color: 'blue' });
var b = new Delta().delete(-1);
var expected = new Delta().delete(-1);
expect(a.compose(b)).to.deep.equal(expected);
});
});
});
2 changes: 1 addition & 1 deletion test/op.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('op', function() {
expect(iter.next()).to.deep.equal(this.delta.ops[i]);
}
expect(iter.next()).to.deep.equal({ retain: Infinity });
// Rerun to test multiple past end next calls
expect(iter.next(4)).to.deep.equal({ retain: 4 });
expect(iter.next()).to.deep.equal({ retain: Infinity });
});

Expand Down

0 comments on commit 9bdc9b2

Please sign in to comment.