From b578c09bc9dab5128feb401065a3b7b17a8ad329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stian=20H=C3=A5klev?= Date: Fri, 12 Jul 2019 14:25:06 +0200 Subject: [PATCH 1/5] Adding presence to our fork --- lib/json0.js | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/lib/json0.js b/lib/json0.js index dc3a405..c7799bb 100644 --- a/lib/json0.js +++ b/lib/json0.js @@ -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); /** From 2d74bcc9e5cbc85b71786f7b6d3779934b8dbc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stian=20H=C3=A5klev?= Date: Fri, 12 Jul 2019 14:40:31 +0200 Subject: [PATCH 2/5] Adding presence to text0 --- lib/text0.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/text0.js b/lib/text0.js index e26c6a9..5d6da44 100644 --- a/lib/text0.js +++ b/lib/text0.js @@ -253,4 +253,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); From a3567b88e499f72b68629545ece0cca6a3ed97c7 Mon Sep 17 00:00:00 2001 From: Stian Haklev Date: Tue, 21 Apr 2020 11:16:26 +0200 Subject: [PATCH 3/5] Updating ownOp even if we are at the same position --- CHANGELOG.MD | 1 + lib/text0.js | 8 +++++++- package.json | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.MD diff --git a/CHANGELOG.MD b/CHANGELOG.MD new file mode 100644 index 0000000..6e93522 --- /dev/null +++ b/CHANGELOG.MD @@ -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 diff --git a/lib/text0.js b/lib/text0.js index 5d6da44..04e32de 100644 --- a/lib/text0.js +++ b/lib/text0.js @@ -143,7 +143,13 @@ text.normalize = function(op) { var transformPosition = 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 && insertAfter)) { + // 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. + if (c.p < pos || (c.p === pos) { // && insertAfter)) {== pos && insertAfter)) { return pos + c.i.length; } else { return pos; diff --git a/package.json b/package.json index b6c9df6..a9d337d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "ot-json0", - "version": "1.1.0", + "name": "@minervaproject/ot-json0", + "version": "1.4.0", "description": "JSON OT type", "main": "lib/index.js", "directories": { From de3a74b8184ca1d43bd8b46cfe171c0ad3526842 Mon Sep 17 00:00:00 2001 From: Stian Haklev Date: Tue, 21 Apr 2020 11:23:36 +0200 Subject: [PATCH 4/5] Bug fix --- lib/text0.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/text0.js b/lib/text0.js index 04e32de..d0093ae 100644 --- a/lib/text0.js +++ b/lib/text0.js @@ -149,7 +149,7 @@ var transformPosition = function(pos, c, insertAfter) { // 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. - if (c.p < pos || (c.p === pos) { // && insertAfter)) {== pos && insertAfter)) { + if (c.p < pos || (c.p === pos)) { // && insertAfter)) {== pos && insertAfter)) { return pos + c.i.length; } else { return pos; diff --git a/package.json b/package.json index a9d337d..2a330e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@minervaproject/ot-json0", - "version": "1.4.0", + "version": "1.4.1", "description": "JSON OT type", "main": "lib/index.js", "directories": { From 7e77b7c90dba6095d11778dad6995ca5ee1932ea Mon Sep 17 00:00:00 2001 From: Stian Haklev Date: Wed, 22 Apr 2020 17:41:16 +0200 Subject: [PATCH 5/5] WIP --- lib/text0.js | 47 +++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/text0.js b/lib/text0.js index d0093ae..3407ebf 100644 --- a/lib/text0.js +++ b/lib/text0.js @@ -143,13 +143,44 @@ text.normalize = function(op) { var transformPosition = function(pos, c, insertAfter) { // This will get collapsed into a giant ternary by uglify. if (c.i != null) { - // 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. - if (c.p < pos || (c.p === pos)) { // && insertAfter)) {== pos && insertAfter)) { + if (c.p < pos || (c.p === pos && insertAfter)) { + 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; + } + } +}; + +// 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; @@ -177,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; diff --git a/package.json b/package.json index 2a330e1..18be7c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@minervaproject/ot-json0", - "version": "1.4.1", + "version": "1.4.2", "description": "JSON OT type", "main": "lib/index.js", "directories": {