Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate out the transformPosition function for presence, and for ot #34

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
21/4/2020 - changing around line 146 in text0 to also transform position when ownOp and position = op to address bugs with code cursors
132 changes: 132 additions & 0 deletions lib/json0.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,138 @@ json.transformComponent = function(dest, c, otherC, type) {
return dest;
};

json.createPresence = function(presenceData) {
return presenceData;
};

json.comparePresence = function(pres1, pres2) {
return JSON.stringify(pres1) === JSON.stringify(pres2);
};

// var transformPosition = function(cursor, op, isOwnOp) {
// var cursor = clone(cursor);
//
// var opIsAncestor = cursor.length >= op.p.length; // true also if op is self
// var opIsSibling = cursor.length === op.p.length; // true also if op is self
// var opIsAncestorSibling = cursor.length >= op.p.length; // true also if op is self or sibling of self
// var equalUpTo = -1;
// for (var i = 0; i < op.p.length; i++) {
// if (op.p[i] !== cursor[i]) {
// opIsAncestor = false;
// if (i < op.p.length - 1) {
// opIsSibling = false;
// opIsAncestorSibling = false;
// }
// }
// if (equalUpTo === i - 1 && op.p[i] === cursor[i]) {
// equalUpTo += 1;
// }
// }
//
// if (opIsSibling) {
// if (op.sd) {
// cursor[cursor.length - 1] = text.transformCursor(
// cursor[cursor.length - 1],
// [{ p: op.p[op.p.length - 1], d: op.sd }],
// isOwnOp ? 'right' : 'left'
// );
// }
// if (op.si) {
// cursor[cursor.length - 1] = text.transformCursor(
// cursor[cursor.length - 1],
// [{ p: op.p[op.p.length - 1], i: op.si }],
// isOwnOp ? 'right' : 'left'
// );
// }
// }
//
// if (opIsAncestor) {
// if (op.lm !== undefined) {
// cursor[equalUpTo] = op.lm;
// }
// if (op.od && op.oi) {
// cursor = op.p.slice(0, op.p.length);
// } else if (op.od) {
// cursor = op.p.slice(0, op.p.length - 1);
// } else if (op.ld && op.li) {
// cursor = op.p.slice(0, op.p.length);
// } else if (op.ld) {
// cursor = op.p.slice(0, op.p.length - 1);
// }
// }
//
// if (opIsAncestorSibling) {
// var lastPathIdx = op.p.length - 1;
// if (
// !opIsAncestor &&
// op.ld &&
// !op.li &&
// op.p[lastPathIdx] < cursor[lastPathIdx]
// ) {
// cursor[lastPathIdx] -= 1;
// } else if (!op.ld && op.li && op.p[lastPathIdx] <= cursor[lastPathIdx]) {
// cursor[lastPathIdx] += 1;
// }
//
// // if move item in list from after to before
// if (
// !opIsAncestor &&
// op.lm !== undefined &&
// op.p[lastPathIdx] > cursor[lastPathIdx] &&
// op.lm <= cursor[lastPathIdx]
// ) {
// cursor[lastPathIdx] += 1;
// // if move item in list from before to after
// } else if (
// !opIsAncestor &&
// op.lm !== undefined &&
// op.p[lastPathIdx] < cursor[lastPathIdx] &&
// op.lm >= cursor[lastPathIdx]
// ) {
// cursor[lastPathIdx] -= 1;
// }
// }
//
// return cursor;
// };
//
// json.transformCursor = function(cursor, op, isOwnOp) {
// for (var i = 0; i < op.length; i++) {
// cursor = transformPosition(cursor, op[i], isOwnOp);
// }
// return cursor;
// };
//

json.transformPresence = function(presence, op, isOwnOp) {
// Don't transform path-only presence objects.
if(!presence.t) return presence;

for (var i = 0; i < op.length; i++) {
var c = op[i];

// convert old string ops to use subtype for backwards compatibility
if (c.si != null || c.sd != null) {
convertFromText(c);
}

// Transform against subtype ops.
if (c.t && c.t === presence.t && json.pathMatches(c.p, presence.p)) {
presence = Object.assign({}, presence, {
s: subtypes[presence.t].transformPresence(presence.s, [c], isOwnOp)
});
}

// convert back to old string ops
if (c.t === 'text0') {
convertToText(c);
}

// TODO transform against non-subtype ops.
};
return presence;
};

require('./bootstrapTransform')(json, json.transformComponent, json.checkValidOp, json.append);

/**
Expand Down
69 changes: 68 additions & 1 deletion lib/text0.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,43 @@ var transformPosition = function(pos, c, insertAfter) {
}
};

// This helper method transforms a position by an op component.
//
// If c is an insert, insertAfter specifies whether the transform
// is pushed after the insert (true) or before it (false).
//
// insertAfter is optional for deletes.
// Stian 21/4/2020: Originally there was insertAfter, but this caused a
// bug where selecting text, then deleting it by typing a letter would get
// misinterpreted. Seems to me that we want to push the cursor even by
// normal typing. Perhaps this is because we don't send position updates
// when typing, while others do? Hopefully changing this doesn't introduce
// other bugs. We duplicated the transformPosition function to not introduce
// bugs into the core OT mechanism.

var transformPositionPresence = function(pos, c, insertAfter) {
// This will get collapsed into a giant ternary by uglify.
if (c.i != null) {
if (c.p < pos || (c.p === pos) {
return pos + c.i.length;
} else {
return pos;
}
} else {
// I think this could also be written as: Math.min(c.p, Math.min(c.p -
// otherC.p, otherC.d.length)) but I think its harder to read that way, and
// it compiles using ternary operators anyway so its no slower written like
// this.
if (pos <= c.p) {
return pos;
} else if (pos <= c.p + c.d.length) {
return c.p;
} else {
return pos - c.d.length;
}
}
};

// Helper method to transform a cursor position as a result of an op.
//
// Like transformPosition above, if c is an insert, insertAfter specifies
Expand All @@ -171,7 +208,7 @@ var transformPosition = function(pos, c, insertAfter) {
text.transformCursor = function(position, op, side) {
var insertAfter = side === 'right';
for (var i = 0; i < op.length; i++) {
position = transformPosition(position, op[i], insertAfter);
position = transformPositionPresence(position, op[i], insertAfter);
}

return position;
Expand Down Expand Up @@ -253,4 +290,34 @@ text.invert = function(op) {
return op;
};

text.createPresence = function(presenceData) {
return presenceData;
};

// Draws from https://github.com/Teamwork/ot-rich-text/blob/master/src/Operation.js
text.transformPresence = function(presence, operation, isOwnOperation) {
var user = presence.u;
var change = presence.c;
var selections = presence.s;
var side = isOwnOperation ? 'right' : 'left';
var newSelections = new Array(selections.length);

for (var i = 0, l = selections.length; i < l; ++i) {
newSelections[i] = [
text.transformCursor(selections[i][0], operation[0].o, side),
text.transformCursor(selections[i][1], operation[0].o, side)
];
}

return {
u: user,
c: change,
s: newSelections
}
}

text.comparePresence = function(pres1, pres2) {
return JSON.stringify(pres1) === JSON.stringify(pres2);
};

require('./bootstrapTransform')(text, transformComponent, checkValidOp, append);
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ot-json0",
"version": "1.1.0",
"name": "@minervaproject/ot-json0",
"version": "1.4.2",
"description": "JSON OT type",
"main": "lib/index.js",
"directories": {
Expand Down