@lexical/yjs 0.12.2 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CollabDecoratorNode.d.ts +0 -1
- package/LexicalYjs.dev.js +49 -335
- package/LexicalYjs.prod.js +2 -2
- package/package.json +3 -3
package/LexicalYjs.dev.js
CHANGED
|
@@ -25,42 +25,33 @@ class CollabLineBreakNode {
|
|
|
25
25
|
this._parent = parent;
|
|
26
26
|
this._type = 'linebreak';
|
|
27
27
|
}
|
|
28
|
-
|
|
29
28
|
getNode() {
|
|
30
29
|
const node = lexical.$getNodeByKey(this._key);
|
|
31
30
|
return lexical.$isLineBreakNode(node) ? node : null;
|
|
32
31
|
}
|
|
33
|
-
|
|
34
32
|
getKey() {
|
|
35
33
|
return this._key;
|
|
36
34
|
}
|
|
37
|
-
|
|
38
35
|
getSharedType() {
|
|
39
36
|
return this._map;
|
|
40
37
|
}
|
|
41
|
-
|
|
42
38
|
getType() {
|
|
43
39
|
return this._type;
|
|
44
40
|
}
|
|
45
|
-
|
|
46
41
|
getSize() {
|
|
47
42
|
return 1;
|
|
48
43
|
}
|
|
49
|
-
|
|
50
44
|
getOffset() {
|
|
51
45
|
const collabElementNode = this._parent;
|
|
52
46
|
return collabElementNode.getChildOffset(this);
|
|
53
47
|
}
|
|
54
|
-
|
|
55
48
|
destroy(binding) {
|
|
56
49
|
const collabNodeMap = binding.collabNodeMap;
|
|
57
50
|
collabNodeMap.delete(this._key);
|
|
58
51
|
}
|
|
59
|
-
|
|
60
52
|
}
|
|
61
53
|
function $createCollabLineBreakNode(map, parent) {
|
|
62
|
-
const collabNode = new CollabLineBreakNode(map, parent);
|
|
63
|
-
|
|
54
|
+
const collabNode = new CollabLineBreakNode(map, parent);
|
|
64
55
|
map._collabNode = collabNode;
|
|
65
56
|
return collabNode;
|
|
66
57
|
}
|
|
@@ -72,29 +63,25 @@ function $createCollabLineBreakNode(map, parent) {
|
|
|
72
63
|
* LICENSE file in the root directory of this source tree.
|
|
73
64
|
*
|
|
74
65
|
*/
|
|
66
|
+
|
|
75
67
|
function simpleDiffWithCursor(a, b, cursor) {
|
|
76
68
|
const aLength = a.length;
|
|
77
69
|
const bLength = b.length;
|
|
78
70
|
let left = 0; // number of same characters counting from left
|
|
79
|
-
|
|
80
71
|
let right = 0; // number of same characters counting from right
|
|
81
72
|
// Iterate left to the right until we find a changed character
|
|
82
73
|
// First iteration considers the current cursor position
|
|
83
|
-
|
|
84
74
|
while (left < aLength && left < bLength && a[left] === b[left] && left < cursor) {
|
|
85
75
|
left++;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
}
|
|
77
|
+
// Iterate right to the left until we find a changed character
|
|
89
78
|
while (right + left < aLength && right + left < bLength && a[aLength - right - 1] === b[bLength - right - 1]) {
|
|
90
79
|
right++;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
80
|
+
}
|
|
81
|
+
// Try to iterate left further to the right without caring about the current cursor position
|
|
94
82
|
while (right + left < aLength && right + left < bLength && a[left] === b[left]) {
|
|
95
83
|
left++;
|
|
96
84
|
}
|
|
97
|
-
|
|
98
85
|
return {
|
|
99
86
|
index: left,
|
|
100
87
|
insert: b.slice(left, bLength - right),
|
|
@@ -109,23 +96,18 @@ function simpleDiffWithCursor(a, b, cursor) {
|
|
|
109
96
|
* LICENSE file in the root directory of this source tree.
|
|
110
97
|
*
|
|
111
98
|
*/
|
|
112
|
-
|
|
113
99
|
function diffTextContentAndApplyDelta(collabNode, key, prevText, nextText) {
|
|
114
100
|
const selection = lexical.$getSelection();
|
|
115
101
|
let cursorOffset = nextText.length;
|
|
116
|
-
|
|
117
102
|
if (lexical.$isRangeSelection(selection) && selection.isCollapsed()) {
|
|
118
103
|
const anchor = selection.anchor;
|
|
119
|
-
|
|
120
104
|
if (anchor.key === key) {
|
|
121
105
|
cursorOffset = anchor.offset;
|
|
122
106
|
}
|
|
123
107
|
}
|
|
124
|
-
|
|
125
108
|
const diff = simpleDiffWithCursor(prevText, nextText, cursorOffset);
|
|
126
109
|
collabNode.spliceText(diff.index, diff.remove, diff.insert);
|
|
127
110
|
}
|
|
128
|
-
|
|
129
111
|
class CollabTextNode {
|
|
130
112
|
constructor(map, text, parent, type) {
|
|
131
113
|
this._key = '';
|
|
@@ -135,64 +117,50 @@ class CollabTextNode {
|
|
|
135
117
|
this._type = type;
|
|
136
118
|
this._normalized = false;
|
|
137
119
|
}
|
|
138
|
-
|
|
139
120
|
getPrevNode(nodeMap) {
|
|
140
121
|
if (nodeMap === null) {
|
|
141
122
|
return null;
|
|
142
123
|
}
|
|
143
|
-
|
|
144
124
|
const node = nodeMap.get(this._key);
|
|
145
125
|
return lexical.$isTextNode(node) ? node : null;
|
|
146
126
|
}
|
|
147
|
-
|
|
148
127
|
getNode() {
|
|
149
128
|
const node = lexical.$getNodeByKey(this._key);
|
|
150
129
|
return lexical.$isTextNode(node) ? node : null;
|
|
151
130
|
}
|
|
152
|
-
|
|
153
131
|
getSharedType() {
|
|
154
132
|
return this._map;
|
|
155
133
|
}
|
|
156
|
-
|
|
157
134
|
getType() {
|
|
158
135
|
return this._type;
|
|
159
136
|
}
|
|
160
|
-
|
|
161
137
|
getKey() {
|
|
162
138
|
return this._key;
|
|
163
139
|
}
|
|
164
|
-
|
|
165
140
|
getSize() {
|
|
166
141
|
return this._text.length + (this._normalized ? 0 : 1);
|
|
167
142
|
}
|
|
168
|
-
|
|
169
143
|
getOffset() {
|
|
170
144
|
const collabElementNode = this._parent;
|
|
171
145
|
return collabElementNode.getChildOffset(this);
|
|
172
146
|
}
|
|
173
|
-
|
|
174
147
|
spliceText(index, delCount, newText) {
|
|
175
148
|
const collabElementNode = this._parent;
|
|
176
149
|
const xmlText = collabElementNode._xmlText;
|
|
177
150
|
const offset = this.getOffset() + 1 + index;
|
|
178
|
-
|
|
179
151
|
if (delCount !== 0) {
|
|
180
152
|
xmlText.delete(offset, delCount);
|
|
181
153
|
}
|
|
182
|
-
|
|
183
154
|
if (newText !== '') {
|
|
184
155
|
xmlText.insert(offset, newText);
|
|
185
156
|
}
|
|
186
157
|
}
|
|
187
|
-
|
|
188
158
|
syncPropertiesAndTextFromLexical(binding, nextLexicalNode, prevNodeMap) {
|
|
189
159
|
const prevLexicalNode = this.getPrevNode(prevNodeMap);
|
|
190
160
|
const nextText = nextLexicalNode.__text;
|
|
191
161
|
syncPropertiesFromLexical(binding, this._map, prevLexicalNode, nextLexicalNode);
|
|
192
|
-
|
|
193
162
|
if (prevLexicalNode !== null) {
|
|
194
163
|
const prevText = prevLexicalNode.__text;
|
|
195
|
-
|
|
196
164
|
if (prevText !== nextText) {
|
|
197
165
|
const key = nextLexicalNode.__key;
|
|
198
166
|
diffTextContentAndApplyDelta(this, key, prevText, nextText);
|
|
@@ -200,32 +168,25 @@ class CollabTextNode {
|
|
|
200
168
|
}
|
|
201
169
|
}
|
|
202
170
|
}
|
|
203
|
-
|
|
204
171
|
syncPropertiesAndTextFromYjs(binding, keysChanged) {
|
|
205
172
|
const lexicalNode = this.getNode();
|
|
206
|
-
|
|
207
173
|
if (!(lexicalNode !== null)) {
|
|
208
174
|
throw Error(`syncPropertiesAndTextFromYjs: cound not find decorator node`);
|
|
209
175
|
}
|
|
210
|
-
|
|
211
176
|
syncPropertiesFromYjs(binding, this._map, lexicalNode, keysChanged);
|
|
212
177
|
const collabText = this._text;
|
|
213
|
-
|
|
214
178
|
if (lexicalNode.__text !== collabText) {
|
|
215
179
|
const writable = lexicalNode.getWritable();
|
|
216
180
|
writable.__text = collabText;
|
|
217
181
|
}
|
|
218
182
|
}
|
|
219
|
-
|
|
220
183
|
destroy(binding) {
|
|
221
184
|
const collabNodeMap = binding.collabNodeMap;
|
|
222
185
|
collabNodeMap.delete(this._key);
|
|
223
186
|
}
|
|
224
|
-
|
|
225
187
|
}
|
|
226
188
|
function $createCollabTextNode(map, text, parent, type) {
|
|
227
|
-
const collabNode = new CollabTextNode(map, text, parent, type);
|
|
228
|
-
|
|
189
|
+
const collabNode = new CollabTextNode(map, text, parent, type);
|
|
229
190
|
map._collabNode = collabNode;
|
|
230
191
|
return collabNode;
|
|
231
192
|
}
|
|
@@ -241,12 +202,10 @@ const baseExcludedProperties = new Set(['__key', '__parent', '__next', '__prev']
|
|
|
241
202
|
const elementExcludedProperties = new Set(['__first', '__last', '__size']);
|
|
242
203
|
const rootExcludedProperties = new Set(['__cachedText']);
|
|
243
204
|
const textExcludedProperties = new Set(['__text']);
|
|
244
|
-
|
|
245
205
|
function isExcludedProperty(name, node, binding) {
|
|
246
206
|
if (baseExcludedProperties.has(name)) {
|
|
247
207
|
return true;
|
|
248
208
|
}
|
|
249
|
-
|
|
250
209
|
if (lexical.$isTextNode(node)) {
|
|
251
210
|
if (textExcludedProperties.has(name)) {
|
|
252
211
|
return true;
|
|
@@ -256,24 +215,20 @@ function isExcludedProperty(name, node, binding) {
|
|
|
256
215
|
return true;
|
|
257
216
|
}
|
|
258
217
|
}
|
|
259
|
-
|
|
260
218
|
const nodeKlass = node.constructor;
|
|
261
219
|
const excludedProperties = binding.excludedProperties.get(nodeKlass);
|
|
262
220
|
return excludedProperties != null && excludedProperties.has(name);
|
|
263
221
|
}
|
|
264
222
|
function $getNodeByKeyOrThrow(key) {
|
|
265
223
|
const node = lexical.$getNodeByKey(key);
|
|
266
|
-
|
|
267
224
|
if (!(node !== null)) {
|
|
268
225
|
throw Error(`could not find node by key`);
|
|
269
226
|
}
|
|
270
|
-
|
|
271
227
|
return node;
|
|
272
228
|
}
|
|
273
229
|
function $createCollabNodeFromLexicalNode(binding, lexicalNode, parent) {
|
|
274
230
|
const nodeType = lexicalNode.__type;
|
|
275
231
|
let collabNode;
|
|
276
|
-
|
|
277
232
|
if (lexical.$isElementNode(lexicalNode)) {
|
|
278
233
|
const xmlText = new yjs.XmlText();
|
|
279
234
|
collabNode = $createCollabElementNode(xmlText, parent, nodeType);
|
|
@@ -297,69 +252,53 @@ function $createCollabNodeFromLexicalNode(binding, lexicalNode, parent) {
|
|
|
297
252
|
throw Error(`Expected text, element, decorator, or linebreak node`);
|
|
298
253
|
}
|
|
299
254
|
}
|
|
300
|
-
|
|
301
255
|
collabNode._key = lexicalNode.__key;
|
|
302
256
|
return collabNode;
|
|
303
257
|
}
|
|
304
|
-
|
|
305
258
|
function getNodeTypeFromSharedType(sharedType) {
|
|
306
259
|
const type = sharedType instanceof yjs.Map ? sharedType.get('__type') : sharedType.getAttribute('__type');
|
|
307
|
-
|
|
308
260
|
if (!(type != null)) {
|
|
309
261
|
throw Error(`Expected shared type to include type attribute`);
|
|
310
262
|
}
|
|
311
|
-
|
|
312
263
|
return type;
|
|
313
264
|
}
|
|
314
|
-
|
|
315
265
|
function getOrInitCollabNodeFromSharedType(binding, sharedType, parent) {
|
|
316
|
-
// @ts-expect-error: internal field
|
|
317
266
|
const collabNode = sharedType._collabNode;
|
|
318
|
-
|
|
319
267
|
if (collabNode === undefined) {
|
|
320
268
|
const registeredNodes = binding.editor._nodes;
|
|
321
269
|
const type = getNodeTypeFromSharedType(sharedType);
|
|
322
270
|
const nodeInfo = registeredNodes.get(type);
|
|
323
|
-
|
|
324
271
|
if (!(nodeInfo !== undefined)) {
|
|
325
272
|
throw Error(`Node ${type} is not registered`);
|
|
326
273
|
}
|
|
327
|
-
|
|
328
274
|
const sharedParent = sharedType.parent;
|
|
329
275
|
const targetParent = parent === undefined && sharedParent !== null ? getOrInitCollabNodeFromSharedType(binding, sharedParent) : parent || null;
|
|
330
|
-
|
|
331
276
|
if (!(targetParent instanceof CollabElementNode)) {
|
|
332
277
|
throw Error(`Expected parent to be a collab element node`);
|
|
333
278
|
}
|
|
334
|
-
|
|
335
279
|
if (sharedType instanceof yjs.XmlText) {
|
|
336
280
|
return $createCollabElementNode(sharedType, targetParent, type);
|
|
337
281
|
} else if (sharedType instanceof yjs.Map) {
|
|
338
282
|
if (type === 'linebreak') {
|
|
339
283
|
return $createCollabLineBreakNode(sharedType, targetParent);
|
|
340
284
|
}
|
|
341
|
-
|
|
342
285
|
return $createCollabTextNode(sharedType, '', targetParent, type);
|
|
343
286
|
} else if (sharedType instanceof yjs.XmlElement) {
|
|
344
287
|
return $createCollabDecoratorNode(sharedType, targetParent, type);
|
|
345
288
|
}
|
|
346
289
|
}
|
|
347
|
-
|
|
348
290
|
return collabNode;
|
|
349
291
|
}
|
|
350
292
|
function createLexicalNodeFromCollabNode(binding, collabNode, parentKey) {
|
|
351
293
|
const type = collabNode.getType();
|
|
352
294
|
const registeredNodes = binding.editor._nodes;
|
|
353
295
|
const nodeInfo = registeredNodes.get(type);
|
|
354
|
-
|
|
355
296
|
if (!(nodeInfo !== undefined)) {
|
|
356
297
|
throw Error(`Node ${type} is not registered`);
|
|
357
298
|
}
|
|
358
|
-
|
|
359
299
|
const lexicalNode = new nodeInfo.klass();
|
|
360
300
|
lexicalNode.__parent = parentKey;
|
|
361
301
|
collabNode._key = lexicalNode.__key;
|
|
362
|
-
|
|
363
302
|
if (collabNode instanceof CollabElementNode) {
|
|
364
303
|
const xmlText = collabNode._xmlText;
|
|
365
304
|
collabNode.syncPropertiesFromYjs(binding, null);
|
|
@@ -370,43 +309,34 @@ function createLexicalNodeFromCollabNode(binding, collabNode, parentKey) {
|
|
|
370
309
|
} else if (collabNode instanceof CollabDecoratorNode) {
|
|
371
310
|
collabNode.syncPropertiesFromYjs(binding, null);
|
|
372
311
|
}
|
|
373
|
-
|
|
374
312
|
binding.collabNodeMap.set(lexicalNode.__key, collabNode);
|
|
375
313
|
return lexicalNode;
|
|
376
314
|
}
|
|
377
315
|
function syncPropertiesFromYjs(binding, sharedType, lexicalNode, keysChanged) {
|
|
378
316
|
const properties = keysChanged === null ? sharedType instanceof yjs.Map ? Array.from(sharedType.keys()) : Object.keys(sharedType.getAttributes()) : Array.from(keysChanged);
|
|
379
317
|
let writableNode;
|
|
380
|
-
|
|
381
318
|
for (let i = 0; i < properties.length; i++) {
|
|
382
319
|
const property = properties[i];
|
|
383
|
-
|
|
384
320
|
if (isExcludedProperty(property, lexicalNode, binding)) {
|
|
385
321
|
continue;
|
|
386
322
|
}
|
|
387
|
-
|
|
388
323
|
const prevValue = lexicalNode[property];
|
|
389
324
|
let nextValue = sharedType instanceof yjs.Map ? sharedType.get(property) : sharedType.getAttribute(property);
|
|
390
|
-
|
|
391
325
|
if (prevValue !== nextValue) {
|
|
392
326
|
if (nextValue instanceof yjs.Doc) {
|
|
393
327
|
const yjsDocMap = binding.docMap;
|
|
394
|
-
|
|
395
328
|
if (prevValue instanceof yjs.Doc) {
|
|
396
329
|
yjsDocMap.delete(prevValue.guid);
|
|
397
330
|
}
|
|
398
|
-
|
|
399
331
|
const nestedEditor = lexical.createEditor();
|
|
400
332
|
const key = nextValue.guid;
|
|
401
333
|
nestedEditor._key = key;
|
|
402
334
|
yjsDocMap.set(key, nextValue);
|
|
403
335
|
nextValue = nestedEditor;
|
|
404
336
|
}
|
|
405
|
-
|
|
406
337
|
if (writableNode === undefined) {
|
|
407
338
|
writableNode = lexicalNode.getWritable();
|
|
408
339
|
}
|
|
409
|
-
|
|
410
340
|
writableNode[property] = nextValue;
|
|
411
341
|
}
|
|
412
342
|
}
|
|
@@ -415,46 +345,40 @@ function syncPropertiesFromLexical(binding, sharedType, prevLexicalNode, nextLex
|
|
|
415
345
|
const type = nextLexicalNode.__type;
|
|
416
346
|
const nodeProperties = binding.nodeProperties;
|
|
417
347
|
let properties = nodeProperties.get(type);
|
|
418
|
-
|
|
419
348
|
if (properties === undefined) {
|
|
420
349
|
properties = Object.keys(nextLexicalNode).filter(property => {
|
|
421
350
|
return !isExcludedProperty(property, nextLexicalNode, binding);
|
|
422
351
|
});
|
|
423
352
|
nodeProperties.set(type, properties);
|
|
424
353
|
}
|
|
425
|
-
|
|
426
354
|
const EditorClass = binding.editor.constructor;
|
|
427
|
-
|
|
428
355
|
for (let i = 0; i < properties.length; i++) {
|
|
429
356
|
const property = properties[i];
|
|
430
357
|
const prevValue = prevLexicalNode === null ? undefined : prevLexicalNode[property];
|
|
431
358
|
let nextValue = nextLexicalNode[property];
|
|
432
|
-
|
|
433
359
|
if (prevValue !== nextValue) {
|
|
434
360
|
if (nextValue instanceof EditorClass) {
|
|
435
361
|
const yjsDocMap = binding.docMap;
|
|
436
362
|
let prevDoc;
|
|
437
|
-
|
|
438
363
|
if (prevValue instanceof EditorClass) {
|
|
439
364
|
// @ts-expect-error Lexical node
|
|
440
365
|
const prevKey = prevValue._key;
|
|
441
366
|
prevDoc = yjsDocMap.get(prevKey);
|
|
442
367
|
yjsDocMap.delete(prevKey);
|
|
443
|
-
}
|
|
444
|
-
|
|
368
|
+
}
|
|
445
369
|
|
|
370
|
+
// If we already have a document, use it.
|
|
446
371
|
const doc = prevDoc || new yjs.Doc();
|
|
447
|
-
const key = doc.guid;
|
|
448
|
-
|
|
372
|
+
const key = doc.guid;
|
|
373
|
+
// @ts-expect-error Lexical node
|
|
449
374
|
nextValue._key = key;
|
|
450
375
|
yjsDocMap.set(key, doc);
|
|
451
|
-
nextValue = doc;
|
|
452
|
-
|
|
376
|
+
nextValue = doc;
|
|
377
|
+
// Mark the node dirty as we've assigned a new key to it
|
|
453
378
|
binding.editor.update(() => {
|
|
454
379
|
nextLexicalNode.markDirty();
|
|
455
380
|
});
|
|
456
381
|
}
|
|
457
|
-
|
|
458
382
|
if (sharedType instanceof yjs.Map) {
|
|
459
383
|
sharedType.set(property, nextValue);
|
|
460
384
|
} else {
|
|
@@ -471,21 +395,17 @@ function getPositionFromElementAndOffset(node, offset, boundaryIsEdge) {
|
|
|
471
395
|
let i = 0;
|
|
472
396
|
const children = node._children;
|
|
473
397
|
const childrenLength = children.length;
|
|
474
|
-
|
|
475
398
|
for (; i < childrenLength; i++) {
|
|
476
399
|
const child = children[i];
|
|
477
400
|
const childOffset = index;
|
|
478
401
|
const size = child.getSize();
|
|
479
402
|
index += size;
|
|
480
403
|
const exceedsBoundary = boundaryIsEdge ? index >= offset : index > offset;
|
|
481
|
-
|
|
482
404
|
if (exceedsBoundary && child instanceof CollabTextNode) {
|
|
483
405
|
let textOffset = offset - childOffset - 1;
|
|
484
|
-
|
|
485
406
|
if (textOffset < 0) {
|
|
486
407
|
textOffset = 0;
|
|
487
408
|
}
|
|
488
|
-
|
|
489
409
|
const diffLength = index - offset;
|
|
490
410
|
return {
|
|
491
411
|
length: diffLength,
|
|
@@ -494,7 +414,6 @@ function getPositionFromElementAndOffset(node, offset, boundaryIsEdge) {
|
|
|
494
414
|
offset: textOffset
|
|
495
415
|
};
|
|
496
416
|
}
|
|
497
|
-
|
|
498
417
|
if (index > offset) {
|
|
499
418
|
return {
|
|
500
419
|
length: 0,
|
|
@@ -511,7 +430,6 @@ function getPositionFromElementAndOffset(node, offset, boundaryIsEdge) {
|
|
|
511
430
|
};
|
|
512
431
|
}
|
|
513
432
|
}
|
|
514
|
-
|
|
515
433
|
return {
|
|
516
434
|
length: 0,
|
|
517
435
|
node: null,
|
|
@@ -523,13 +441,13 @@ function doesSelectionNeedRecovering(selection) {
|
|
|
523
441
|
const anchor = selection.anchor;
|
|
524
442
|
const focus = selection.focus;
|
|
525
443
|
let recoveryNeeded = false;
|
|
526
|
-
|
|
527
444
|
try {
|
|
528
445
|
const anchorNode = anchor.getNode();
|
|
529
446
|
const focusNode = focus.getNode();
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
!anchorNode.isAttached() || !focusNode.isAttached() ||
|
|
447
|
+
if (
|
|
448
|
+
// We might have removed a node that no longer exists
|
|
449
|
+
!anchorNode.isAttached() || !focusNode.isAttached() ||
|
|
450
|
+
// If we've split a node, then the offset might not be right
|
|
533
451
|
lexical.$isTextNode(anchorNode) && anchor.offset > anchorNode.getTextContentSize() || lexical.$isTextNode(focusNode) && focus.offset > focusNode.getTextContentSize()) {
|
|
534
452
|
recoveryNeeded = true;
|
|
535
453
|
}
|
|
@@ -538,7 +456,6 @@ function doesSelectionNeedRecovering(selection) {
|
|
|
538
456
|
// an error, so we need recovery then too.
|
|
539
457
|
recoveryNeeded = true;
|
|
540
458
|
}
|
|
541
|
-
|
|
542
459
|
return recoveryNeeded;
|
|
543
460
|
}
|
|
544
461
|
function syncWithTransaction(binding, fn) {
|
|
@@ -547,31 +464,26 @@ function syncWithTransaction(binding, fn) {
|
|
|
547
464
|
function createChildrenArray(element, nodeMap) {
|
|
548
465
|
const children = [];
|
|
549
466
|
let nodeKey = element.__first;
|
|
550
|
-
|
|
551
467
|
while (nodeKey !== null) {
|
|
552
468
|
const node = nodeMap === null ? lexical.$getNodeByKey(nodeKey) : nodeMap.get(nodeKey);
|
|
553
|
-
|
|
554
469
|
if (node === null || node === undefined) {
|
|
555
470
|
{
|
|
556
471
|
throw Error(`createChildrenArray: node does not exist in nodeMap`);
|
|
557
472
|
}
|
|
558
473
|
}
|
|
559
|
-
|
|
560
474
|
children.push(nodeKey);
|
|
561
475
|
nodeKey = node.__next;
|
|
562
476
|
}
|
|
563
|
-
|
|
564
477
|
return children;
|
|
565
478
|
}
|
|
566
479
|
function removeFromParent(node) {
|
|
567
480
|
const oldParent = node.getParent();
|
|
568
|
-
|
|
569
481
|
if (oldParent !== null) {
|
|
570
482
|
const writableNode = node.getWritable();
|
|
571
483
|
const writableParent = oldParent.getWritable();
|
|
572
484
|
const prevSibling = node.getPreviousSibling();
|
|
573
|
-
const nextSibling = node.getNextSibling();
|
|
574
|
-
|
|
485
|
+
const nextSibling = node.getNextSibling();
|
|
486
|
+
// TODO: this function duplicates a bunch of operations, can be simplified.
|
|
575
487
|
if (prevSibling === null) {
|
|
576
488
|
if (nextSibling !== null) {
|
|
577
489
|
const writableNextSibling = nextSibling.getWritable();
|
|
@@ -582,7 +494,6 @@ function removeFromParent(node) {
|
|
|
582
494
|
}
|
|
583
495
|
} else {
|
|
584
496
|
const writablePrevSibling = prevSibling.getWritable();
|
|
585
|
-
|
|
586
497
|
if (nextSibling !== null) {
|
|
587
498
|
const writableNextSibling = nextSibling.getWritable();
|
|
588
499
|
writableNextSibling.__prev = writablePrevSibling.__key;
|
|
@@ -590,10 +501,8 @@ function removeFromParent(node) {
|
|
|
590
501
|
} else {
|
|
591
502
|
writablePrevSibling.__next = null;
|
|
592
503
|
}
|
|
593
|
-
|
|
594
504
|
writableNode.__prev = null;
|
|
595
505
|
}
|
|
596
|
-
|
|
597
506
|
if (nextSibling === null) {
|
|
598
507
|
if (prevSibling !== null) {
|
|
599
508
|
const writablePrevSibling = prevSibling.getWritable();
|
|
@@ -604,7 +513,6 @@ function removeFromParent(node) {
|
|
|
604
513
|
}
|
|
605
514
|
} else {
|
|
606
515
|
const writableNextSibling = nextSibling.getWritable();
|
|
607
|
-
|
|
608
516
|
if (prevSibling !== null) {
|
|
609
517
|
const writablePrevSibling = prevSibling.getWritable();
|
|
610
518
|
writablePrevSibling.__next = writableNextSibling.__key;
|
|
@@ -612,10 +520,8 @@ function removeFromParent(node) {
|
|
|
612
520
|
} else {
|
|
613
521
|
writableNextSibling.__prev = null;
|
|
614
522
|
}
|
|
615
|
-
|
|
616
523
|
writableNode.__next = null;
|
|
617
524
|
}
|
|
618
|
-
|
|
619
525
|
writableParent.__size--;
|
|
620
526
|
writableNode.__parent = null;
|
|
621
527
|
}
|
|
@@ -634,74 +540,54 @@ class CollabDecoratorNode {
|
|
|
634
540
|
this._xmlElem = xmlElem;
|
|
635
541
|
this._parent = parent;
|
|
636
542
|
this._type = type;
|
|
637
|
-
this._unobservers = new Set();
|
|
638
543
|
}
|
|
639
|
-
|
|
640
544
|
getPrevNode(nodeMap) {
|
|
641
545
|
if (nodeMap === null) {
|
|
642
546
|
return null;
|
|
643
547
|
}
|
|
644
|
-
|
|
645
548
|
const node = nodeMap.get(this._key);
|
|
646
549
|
return lexical.$isDecoratorNode(node) ? node : null;
|
|
647
550
|
}
|
|
648
|
-
|
|
649
551
|
getNode() {
|
|
650
552
|
const node = lexical.$getNodeByKey(this._key);
|
|
651
553
|
return lexical.$isDecoratorNode(node) ? node : null;
|
|
652
554
|
}
|
|
653
|
-
|
|
654
555
|
getSharedType() {
|
|
655
556
|
return this._xmlElem;
|
|
656
557
|
}
|
|
657
|
-
|
|
658
558
|
getType() {
|
|
659
559
|
return this._type;
|
|
660
560
|
}
|
|
661
|
-
|
|
662
561
|
getKey() {
|
|
663
562
|
return this._key;
|
|
664
563
|
}
|
|
665
|
-
|
|
666
564
|
getSize() {
|
|
667
565
|
return 1;
|
|
668
566
|
}
|
|
669
|
-
|
|
670
567
|
getOffset() {
|
|
671
568
|
const collabElementNode = this._parent;
|
|
672
569
|
return collabElementNode.getChildOffset(this);
|
|
673
570
|
}
|
|
674
|
-
|
|
675
571
|
syncPropertiesFromLexical(binding, nextLexicalNode, prevNodeMap) {
|
|
676
572
|
const prevLexicalNode = this.getPrevNode(prevNodeMap);
|
|
677
573
|
const xmlElem = this._xmlElem;
|
|
678
574
|
syncPropertiesFromLexical(binding, xmlElem, prevLexicalNode, nextLexicalNode);
|
|
679
575
|
}
|
|
680
|
-
|
|
681
576
|
syncPropertiesFromYjs(binding, keysChanged) {
|
|
682
577
|
const lexicalNode = this.getNode();
|
|
683
|
-
|
|
684
578
|
if (!(lexicalNode !== null)) {
|
|
685
579
|
throw Error(`syncPropertiesFromYjs: cound not find decorator node`);
|
|
686
580
|
}
|
|
687
|
-
|
|
688
581
|
const xmlElem = this._xmlElem;
|
|
689
582
|
syncPropertiesFromYjs(binding, xmlElem, lexicalNode, keysChanged);
|
|
690
583
|
}
|
|
691
|
-
|
|
692
584
|
destroy(binding) {
|
|
693
585
|
const collabNodeMap = binding.collabNodeMap;
|
|
694
586
|
collabNodeMap.delete(this._key);
|
|
695
|
-
|
|
696
|
-
this._unobservers.forEach(unobserver => unobserver());
|
|
697
|
-
|
|
698
|
-
this._unobservers.clear();
|
|
699
587
|
}
|
|
700
|
-
|
|
701
588
|
}
|
|
702
589
|
function $createCollabDecoratorNode(xmlElem, parent, type) {
|
|
703
|
-
const collabNode = new CollabDecoratorNode(xmlElem, parent, type);
|
|
704
|
-
|
|
590
|
+
const collabNode = new CollabDecoratorNode(xmlElem, parent, type);
|
|
705
591
|
xmlElem._collabNode = collabNode;
|
|
706
592
|
return collabNode;
|
|
707
593
|
}
|
|
@@ -721,75 +607,57 @@ class CollabElementNode {
|
|
|
721
607
|
this._type = type;
|
|
722
608
|
this._parent = parent;
|
|
723
609
|
}
|
|
724
|
-
|
|
725
610
|
getPrevNode(nodeMap) {
|
|
726
611
|
if (nodeMap === null) {
|
|
727
612
|
return null;
|
|
728
613
|
}
|
|
729
|
-
|
|
730
614
|
const node = nodeMap.get(this._key);
|
|
731
615
|
return lexical.$isElementNode(node) ? node : null;
|
|
732
616
|
}
|
|
733
|
-
|
|
734
617
|
getNode() {
|
|
735
618
|
const node = lexical.$getNodeByKey(this._key);
|
|
736
619
|
return lexical.$isElementNode(node) ? node : null;
|
|
737
620
|
}
|
|
738
|
-
|
|
739
621
|
getSharedType() {
|
|
740
622
|
return this._xmlText;
|
|
741
623
|
}
|
|
742
|
-
|
|
743
624
|
getType() {
|
|
744
625
|
return this._type;
|
|
745
626
|
}
|
|
746
|
-
|
|
747
627
|
getKey() {
|
|
748
628
|
return this._key;
|
|
749
629
|
}
|
|
750
|
-
|
|
751
630
|
isEmpty() {
|
|
752
631
|
return this._children.length === 0;
|
|
753
632
|
}
|
|
754
|
-
|
|
755
633
|
getSize() {
|
|
756
634
|
return 1;
|
|
757
635
|
}
|
|
758
|
-
|
|
759
636
|
getOffset() {
|
|
760
637
|
const collabElementNode = this._parent;
|
|
761
|
-
|
|
762
638
|
if (!(collabElementNode !== null)) {
|
|
763
639
|
throw Error(`getOffset: cound not find collab element node`);
|
|
764
640
|
}
|
|
765
|
-
|
|
766
641
|
return collabElementNode.getChildOffset(this);
|
|
767
642
|
}
|
|
768
|
-
|
|
769
643
|
syncPropertiesFromYjs(binding, keysChanged) {
|
|
770
644
|
const lexicalNode = this.getNode();
|
|
771
|
-
|
|
772
645
|
if (!(lexicalNode !== null)) {
|
|
773
646
|
throw Error(`syncPropertiesFromYjs: cound not find element node`);
|
|
774
647
|
}
|
|
775
|
-
|
|
776
648
|
syncPropertiesFromYjs(binding, this._xmlText, lexicalNode, keysChanged);
|
|
777
649
|
}
|
|
778
|
-
|
|
779
650
|
applyChildrenYjsDelta(binding, deltas) {
|
|
780
651
|
const children = this._children;
|
|
781
652
|
let currIndex = 0;
|
|
782
|
-
|
|
783
653
|
for (let i = 0; i < deltas.length; i++) {
|
|
784
654
|
const delta = deltas[i];
|
|
785
655
|
const insertDelta = delta.insert;
|
|
786
656
|
const deleteDelta = delta.delete;
|
|
787
|
-
|
|
788
657
|
if (delta.retain != null) {
|
|
789
658
|
currIndex += delta.retain;
|
|
790
659
|
} else if (typeof deleteDelta === 'number') {
|
|
791
660
|
let deletionSize = deleteDelta;
|
|
792
|
-
|
|
793
661
|
while (deletionSize > 0) {
|
|
794
662
|
const {
|
|
795
663
|
node,
|
|
@@ -797,7 +665,6 @@ class CollabElementNode {
|
|
|
797
665
|
offset,
|
|
798
666
|
length
|
|
799
667
|
} = getPositionFromElementAndOffset(this, currIndex, false);
|
|
800
|
-
|
|
801
668
|
if (node instanceof CollabElementNode || node instanceof CollabLineBreakNode || node instanceof CollabDecoratorNode) {
|
|
802
669
|
children.splice(nodeIndex, 1);
|
|
803
670
|
deletionSize -= 1;
|
|
@@ -805,8 +672,8 @@ class CollabElementNode {
|
|
|
805
672
|
const delCount = Math.min(deletionSize, length);
|
|
806
673
|
const prevCollabNode = nodeIndex !== 0 ? children[nodeIndex - 1] : null;
|
|
807
674
|
const nodeSize = node.getSize();
|
|
808
|
-
|
|
809
|
-
|
|
675
|
+
if (offset === 0 && delCount === 1 && nodeIndex > 0 && prevCollabNode instanceof CollabTextNode && length === nodeSize &&
|
|
676
|
+
// If the node has no keys, it's been deleted
|
|
810
677
|
Array.from(node._map.keys()).length === 0) {
|
|
811
678
|
// Merge the text node with previous.
|
|
812
679
|
prevCollabNode._text += node._text;
|
|
@@ -817,7 +684,6 @@ class CollabElementNode {
|
|
|
817
684
|
} else {
|
|
818
685
|
node._text = spliceString(node._text, offset, delCount, '');
|
|
819
686
|
}
|
|
820
|
-
|
|
821
687
|
deletionSize -= delCount;
|
|
822
688
|
} else {
|
|
823
689
|
// Can occur due to the deletion from the dangling text heuristic below.
|
|
@@ -830,20 +696,19 @@ class CollabElementNode {
|
|
|
830
696
|
node,
|
|
831
697
|
offset
|
|
832
698
|
} = getPositionFromElementAndOffset(this, currIndex, true);
|
|
833
|
-
|
|
834
699
|
if (node instanceof CollabTextNode) {
|
|
835
700
|
node._text = spliceString(node._text, offset, 0, insertDelta);
|
|
836
701
|
} else {
|
|
837
702
|
// TODO: maybe we can improve this by keeping around a redundant
|
|
838
703
|
// text node map, rather than removing all the text nodes, so there
|
|
839
704
|
// never can be dangling text.
|
|
705
|
+
|
|
840
706
|
// We have a conflict where there was likely a CollabTextNode and
|
|
841
707
|
// an Lexical TextNode too, but they were removed in a merge. So
|
|
842
708
|
// let's just ignore the text and trigger a removal for it from our
|
|
843
709
|
// shared type.
|
|
844
710
|
this._xmlText.delete(offset, insertDelta.length);
|
|
845
711
|
}
|
|
846
|
-
|
|
847
712
|
currIndex += insertDelta.length;
|
|
848
713
|
} else {
|
|
849
714
|
const sharedType = insertDelta;
|
|
@@ -859,15 +724,12 @@ class CollabElementNode {
|
|
|
859
724
|
}
|
|
860
725
|
}
|
|
861
726
|
}
|
|
862
|
-
|
|
863
727
|
syncChildrenFromYjs(binding) {
|
|
864
728
|
// Now diff the children of the collab node with that of our existing Lexical node.
|
|
865
729
|
const lexicalNode = this.getNode();
|
|
866
|
-
|
|
867
730
|
if (!(lexicalNode !== null)) {
|
|
868
731
|
throw Error(`syncChildrenFromYjs: cound not find element node`);
|
|
869
732
|
}
|
|
870
|
-
|
|
871
733
|
const key = lexicalNode.__key;
|
|
872
734
|
const prevLexicalChildrenKeys = createChildrenArray(lexicalNode, null);
|
|
873
735
|
const lexicalChildrenKeysLength = prevLexicalChildrenKeys.length;
|
|
@@ -879,25 +741,20 @@ class CollabElementNode {
|
|
|
879
741
|
let writableLexicalNode;
|
|
880
742
|
let prevIndex = 0;
|
|
881
743
|
let prevChildNode = null;
|
|
882
|
-
|
|
883
744
|
if (collabChildrenLength !== lexicalChildrenKeysLength) {
|
|
884
745
|
writableLexicalNode = lexicalNode.getWritable();
|
|
885
746
|
}
|
|
886
|
-
|
|
887
747
|
for (let i = 0; i < collabChildrenLength; i++) {
|
|
888
748
|
const lexicalChildKey = prevLexicalChildrenKeys[prevIndex];
|
|
889
749
|
const childCollabNode = collabChildren[i];
|
|
890
750
|
const collabLexicalChildNode = childCollabNode.getNode();
|
|
891
751
|
const collabKey = childCollabNode._key;
|
|
892
|
-
|
|
893
752
|
if (collabLexicalChildNode !== null && lexicalChildKey === collabKey) {
|
|
894
|
-
const childNeedsUpdating = lexical.$isTextNode(collabLexicalChildNode);
|
|
895
|
-
|
|
753
|
+
const childNeedsUpdating = lexical.$isTextNode(collabLexicalChildNode);
|
|
754
|
+
// Update
|
|
896
755
|
visitedKeys.add(lexicalChildKey);
|
|
897
|
-
|
|
898
756
|
if (childNeedsUpdating) {
|
|
899
757
|
childCollabNode._key = lexicalChildKey;
|
|
900
|
-
|
|
901
758
|
if (childCollabNode instanceof CollabElementNode) {
|
|
902
759
|
const xmlText = childCollabNode._xmlText;
|
|
903
760
|
childCollabNode.syncPropertiesFromYjs(binding, null);
|
|
@@ -918,17 +775,14 @@ class CollabElementNode {
|
|
|
918
775
|
} else {
|
|
919
776
|
if (collabKeys === undefined) {
|
|
920
777
|
collabKeys = new Set();
|
|
921
|
-
|
|
922
778
|
for (let s = 0; s < collabChildrenLength; s++) {
|
|
923
779
|
const child = collabChildren[s];
|
|
924
780
|
const childKey = child._key;
|
|
925
|
-
|
|
926
781
|
if (childKey !== '') {
|
|
927
782
|
collabKeys.add(childKey);
|
|
928
783
|
}
|
|
929
784
|
}
|
|
930
785
|
}
|
|
931
|
-
|
|
932
786
|
if (collabLexicalChildNode !== null && lexicalChildKey !== undefined && !collabKeys.has(lexicalChildKey)) {
|
|
933
787
|
const nodeToRemove = $getNodeByKeyOrThrow(lexicalChildKey);
|
|
934
788
|
removeFromParent(nodeToRemove);
|
|
@@ -936,17 +790,14 @@ class CollabElementNode {
|
|
|
936
790
|
prevIndex++;
|
|
937
791
|
continue;
|
|
938
792
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
793
|
+
writableLexicalNode = lexicalNode.getWritable();
|
|
794
|
+
// Create/Replace
|
|
942
795
|
const lexicalChildNode = createLexicalNodeFromCollabNode(binding, childCollabNode, key);
|
|
943
796
|
const childKey = lexicalChildNode.__key;
|
|
944
797
|
collabNodeMap.set(childKey, childCollabNode);
|
|
945
|
-
|
|
946
798
|
if (prevChildNode === null) {
|
|
947
799
|
const nextSibling = writableLexicalNode.getFirstChild();
|
|
948
800
|
writableLexicalNode.__first = childKey;
|
|
949
|
-
|
|
950
801
|
if (nextSibling !== null) {
|
|
951
802
|
const writableNextSibling = nextSibling.getWritable();
|
|
952
803
|
writableNextSibling.__prev = childKey;
|
|
@@ -957,49 +808,39 @@ class CollabElementNode {
|
|
|
957
808
|
const nextSibling = prevChildNode.getNextSibling();
|
|
958
809
|
writablePrevChildNode.__next = childKey;
|
|
959
810
|
lexicalChildNode.__prev = prevChildNode.__key;
|
|
960
|
-
|
|
961
811
|
if (nextSibling !== null) {
|
|
962
812
|
const writableNextSibling = nextSibling.getWritable();
|
|
963
813
|
writableNextSibling.__prev = childKey;
|
|
964
814
|
lexicalChildNode.__next = writableNextSibling.__key;
|
|
965
815
|
}
|
|
966
816
|
}
|
|
967
|
-
|
|
968
817
|
if (i === collabChildrenLength - 1) {
|
|
969
818
|
writableLexicalNode.__last = childKey;
|
|
970
819
|
}
|
|
971
|
-
|
|
972
820
|
writableLexicalNode.__size++;
|
|
973
821
|
prevChildNode = lexicalChildNode;
|
|
974
822
|
}
|
|
975
823
|
}
|
|
976
|
-
|
|
977
824
|
for (let i = 0; i < lexicalChildrenKeysLength; i++) {
|
|
978
825
|
const lexicalChildKey = prevLexicalChildrenKeys[i];
|
|
979
|
-
|
|
980
826
|
if (!visitedKeys.has(lexicalChildKey)) {
|
|
981
827
|
// Remove
|
|
982
828
|
const lexicalChildNode = $getNodeByKeyOrThrow(lexicalChildKey);
|
|
983
829
|
const collabNode = binding.collabNodeMap.get(lexicalChildKey);
|
|
984
|
-
|
|
985
830
|
if (collabNode !== undefined) {
|
|
986
831
|
collabNode.destroy(binding);
|
|
987
832
|
}
|
|
988
|
-
|
|
989
833
|
removeFromParent(lexicalChildNode);
|
|
990
834
|
}
|
|
991
835
|
}
|
|
992
836
|
}
|
|
993
|
-
|
|
994
837
|
syncPropertiesFromLexical(binding, nextLexicalNode, prevNodeMap) {
|
|
995
838
|
syncPropertiesFromLexical(binding, this._xmlText, this.getPrevNode(prevNodeMap), nextLexicalNode);
|
|
996
839
|
}
|
|
997
|
-
|
|
998
840
|
_syncChildFromLexical(binding, index, key, prevNodeMap, dirtyElements, dirtyLeaves) {
|
|
999
|
-
const childCollabNode = this._children[index];
|
|
1000
|
-
|
|
841
|
+
const childCollabNode = this._children[index];
|
|
842
|
+
// Update
|
|
1001
843
|
const nextChildNode = $getNodeByKeyOrThrow(key);
|
|
1002
|
-
|
|
1003
844
|
if (childCollabNode instanceof CollabElementNode && lexical.$isElementNode(nextChildNode)) {
|
|
1004
845
|
childCollabNode.syncPropertiesFromLexical(binding, nextChildNode, prevNodeMap);
|
|
1005
846
|
childCollabNode.syncChildrenFromLexical(binding, nextChildNode, prevNodeMap, dirtyElements, dirtyLeaves);
|
|
@@ -1009,7 +850,6 @@ class CollabElementNode {
|
|
|
1009
850
|
childCollabNode.syncPropertiesFromLexical(binding, nextChildNode, prevNodeMap);
|
|
1010
851
|
}
|
|
1011
852
|
}
|
|
1012
|
-
|
|
1013
853
|
syncChildrenFromLexical(binding, nextLexicalNode, prevNodeMap, dirtyElements, dirtyLeaves) {
|
|
1014
854
|
const prevLexicalNode = this.getPrevNode(prevNodeMap);
|
|
1015
855
|
const prevChildren = prevLexicalNode === null ? [] : createChildrenArray(prevLexicalNode, prevNodeMap);
|
|
@@ -1021,29 +861,23 @@ class CollabElementNode {
|
|
|
1021
861
|
let nextChildrenSet;
|
|
1022
862
|
let prevIndex = 0;
|
|
1023
863
|
let nextIndex = 0;
|
|
1024
|
-
|
|
1025
864
|
while (prevIndex <= prevEndIndex && nextIndex <= nextEndIndex) {
|
|
1026
865
|
const prevKey = prevChildren[prevIndex];
|
|
1027
866
|
const nextKey = nextChildren[nextIndex];
|
|
1028
|
-
|
|
1029
867
|
if (prevKey === nextKey) {
|
|
1030
868
|
// Nove move, create or remove
|
|
1031
869
|
this._syncChildFromLexical(binding, nextIndex, nextKey, prevNodeMap, dirtyElements, dirtyLeaves);
|
|
1032
|
-
|
|
1033
870
|
prevIndex++;
|
|
1034
871
|
nextIndex++;
|
|
1035
872
|
} else {
|
|
1036
873
|
if (prevChildrenSet === undefined) {
|
|
1037
874
|
prevChildrenSet = new Set(prevChildren);
|
|
1038
875
|
}
|
|
1039
|
-
|
|
1040
876
|
if (nextChildrenSet === undefined) {
|
|
1041
877
|
nextChildrenSet = new Set(nextChildren);
|
|
1042
878
|
}
|
|
1043
|
-
|
|
1044
879
|
const nextHasPrevKey = nextChildrenSet.has(prevKey);
|
|
1045
880
|
const prevHasNextKey = prevChildrenSet.has(nextKey);
|
|
1046
|
-
|
|
1047
881
|
if (!nextHasPrevKey) {
|
|
1048
882
|
// Remove
|
|
1049
883
|
this.splice(binding, nextIndex, 1);
|
|
@@ -1053,7 +887,6 @@ class CollabElementNode {
|
|
|
1053
887
|
const nextChildNode = $getNodeByKeyOrThrow(nextKey);
|
|
1054
888
|
const collabNode = $createCollabNodeFromLexicalNode(binding, nextChildNode, this);
|
|
1055
889
|
collabNodeMap.set(nextKey, collabNode);
|
|
1056
|
-
|
|
1057
890
|
if (prevHasNextKey) {
|
|
1058
891
|
this.splice(binding, nextIndex, 1, collabNode);
|
|
1059
892
|
prevIndex++;
|
|
@@ -1065,10 +898,8 @@ class CollabElementNode {
|
|
|
1065
898
|
}
|
|
1066
899
|
}
|
|
1067
900
|
}
|
|
1068
|
-
|
|
1069
901
|
const appendNewChildren = prevIndex > prevEndIndex;
|
|
1070
902
|
const removeOldChildren = nextIndex > nextEndIndex;
|
|
1071
|
-
|
|
1072
903
|
if (appendNewChildren && !removeOldChildren) {
|
|
1073
904
|
for (; nextIndex <= nextEndIndex; ++nextIndex) {
|
|
1074
905
|
const key = nextChildren[nextIndex];
|
|
@@ -1083,122 +914,94 @@ class CollabElementNode {
|
|
|
1083
914
|
}
|
|
1084
915
|
}
|
|
1085
916
|
}
|
|
1086
|
-
|
|
1087
917
|
append(collabNode) {
|
|
1088
918
|
const xmlText = this._xmlText;
|
|
1089
919
|
const children = this._children;
|
|
1090
920
|
const lastChild = children[children.length - 1];
|
|
1091
921
|
const offset = lastChild !== undefined ? lastChild.getOffset() + lastChild.getSize() : 0;
|
|
1092
|
-
|
|
1093
922
|
if (collabNode instanceof CollabElementNode) {
|
|
1094
923
|
xmlText.insertEmbed(offset, collabNode._xmlText);
|
|
1095
924
|
} else if (collabNode instanceof CollabTextNode) {
|
|
1096
925
|
const map = collabNode._map;
|
|
1097
|
-
|
|
1098
926
|
if (map.parent === null) {
|
|
1099
927
|
xmlText.insertEmbed(offset, map);
|
|
1100
928
|
}
|
|
1101
|
-
|
|
1102
929
|
xmlText.insert(offset + 1, collabNode._text);
|
|
1103
930
|
} else if (collabNode instanceof CollabLineBreakNode) {
|
|
1104
931
|
xmlText.insertEmbed(offset, collabNode._map);
|
|
1105
932
|
} else if (collabNode instanceof CollabDecoratorNode) {
|
|
1106
933
|
xmlText.insertEmbed(offset, collabNode._xmlElem);
|
|
1107
934
|
}
|
|
1108
|
-
|
|
1109
935
|
this._children.push(collabNode);
|
|
1110
936
|
}
|
|
1111
|
-
|
|
1112
937
|
splice(binding, index, delCount, collabNode) {
|
|
1113
938
|
const children = this._children;
|
|
1114
939
|
const child = children[index];
|
|
1115
|
-
|
|
1116
940
|
if (child === undefined) {
|
|
1117
941
|
if (!(collabNode !== undefined)) {
|
|
1118
942
|
throw Error(`splice: could not find collab element node`);
|
|
1119
943
|
}
|
|
1120
|
-
|
|
1121
944
|
this.append(collabNode);
|
|
1122
945
|
return;
|
|
1123
946
|
}
|
|
1124
|
-
|
|
1125
947
|
const offset = child.getOffset();
|
|
1126
|
-
|
|
1127
948
|
if (!(offset !== -1)) {
|
|
1128
949
|
throw Error(`splice: expected offset to be greater than zero`);
|
|
1129
950
|
}
|
|
1130
|
-
|
|
1131
951
|
const xmlText = this._xmlText;
|
|
1132
|
-
|
|
1133
952
|
if (delCount !== 0) {
|
|
1134
953
|
// What if we delete many nodes, don't we need to get all their
|
|
1135
954
|
// sizes?
|
|
1136
955
|
xmlText.delete(offset, child.getSize());
|
|
1137
956
|
}
|
|
1138
|
-
|
|
1139
957
|
if (collabNode instanceof CollabElementNode) {
|
|
1140
958
|
xmlText.insertEmbed(offset, collabNode._xmlText);
|
|
1141
959
|
} else if (collabNode instanceof CollabTextNode) {
|
|
1142
960
|
const map = collabNode._map;
|
|
1143
|
-
|
|
1144
961
|
if (map.parent === null) {
|
|
1145
962
|
xmlText.insertEmbed(offset, map);
|
|
1146
963
|
}
|
|
1147
|
-
|
|
1148
964
|
xmlText.insert(offset + 1, collabNode._text);
|
|
1149
965
|
} else if (collabNode instanceof CollabLineBreakNode) {
|
|
1150
966
|
xmlText.insertEmbed(offset, collabNode._map);
|
|
1151
967
|
} else if (collabNode instanceof CollabDecoratorNode) {
|
|
1152
968
|
xmlText.insertEmbed(offset, collabNode._xmlElem);
|
|
1153
969
|
}
|
|
1154
|
-
|
|
1155
970
|
if (delCount !== 0) {
|
|
1156
971
|
const childrenToDelete = children.slice(index, index + delCount);
|
|
1157
|
-
|
|
1158
972
|
for (let i = 0; i < childrenToDelete.length; i++) {
|
|
1159
973
|
childrenToDelete[i].destroy(binding);
|
|
1160
974
|
}
|
|
1161
975
|
}
|
|
1162
|
-
|
|
1163
976
|
if (collabNode !== undefined) {
|
|
1164
977
|
children.splice(index, delCount, collabNode);
|
|
1165
978
|
} else {
|
|
1166
979
|
children.splice(index, delCount);
|
|
1167
980
|
}
|
|
1168
981
|
}
|
|
1169
|
-
|
|
1170
982
|
getChildOffset(collabNode) {
|
|
1171
983
|
let offset = 0;
|
|
1172
984
|
const children = this._children;
|
|
1173
|
-
|
|
1174
985
|
for (let i = 0; i < children.length; i++) {
|
|
1175
986
|
const child = children[i];
|
|
1176
|
-
|
|
1177
987
|
if (child === collabNode) {
|
|
1178
988
|
return offset;
|
|
1179
989
|
}
|
|
1180
|
-
|
|
1181
990
|
offset += child.getSize();
|
|
1182
991
|
}
|
|
1183
|
-
|
|
1184
992
|
return -1;
|
|
1185
993
|
}
|
|
1186
|
-
|
|
1187
994
|
destroy(binding) {
|
|
1188
995
|
const collabNodeMap = binding.collabNodeMap;
|
|
1189
996
|
const children = this._children;
|
|
1190
|
-
|
|
1191
997
|
for (let i = 0; i < children.length; i++) {
|
|
1192
998
|
children[i].destroy(binding);
|
|
1193
999
|
}
|
|
1194
|
-
|
|
1195
1000
|
collabNodeMap.delete(this._key);
|
|
1196
1001
|
}
|
|
1197
|
-
|
|
1198
1002
|
}
|
|
1199
1003
|
function $createCollabElementNode(xmlText, parent, type) {
|
|
1200
|
-
const collabNode = new CollabElementNode(xmlText, parent, type);
|
|
1201
|
-
|
|
1004
|
+
const collabNode = new CollabElementNode(xmlText, parent, type);
|
|
1202
1005
|
xmlText._collabNode = collabNode;
|
|
1203
1006
|
return collabNode;
|
|
1204
1007
|
}
|
|
@@ -1214,7 +1017,6 @@ function createBinding(editor, provider, id, doc, docMap, excludedProperties) {
|
|
|
1214
1017
|
if (!(doc !== undefined && doc !== null)) {
|
|
1215
1018
|
throw Error(`createBinding: doc is null or undefined`);
|
|
1216
1019
|
}
|
|
1217
|
-
|
|
1218
1020
|
const rootXmlText = doc.get('root', yjs.XmlText);
|
|
1219
1021
|
const root = $createCollabElementNode(rootXmlText, null, 'root');
|
|
1220
1022
|
root._key = 'root';
|
|
@@ -1240,53 +1042,41 @@ function createBinding(editor, provider, id, doc, docMap, excludedProperties) {
|
|
|
1240
1042
|
* LICENSE file in the root directory of this source tree.
|
|
1241
1043
|
*
|
|
1242
1044
|
*/
|
|
1243
|
-
|
|
1244
1045
|
function createRelativePosition(point, binding) {
|
|
1245
1046
|
const collabNodeMap = binding.collabNodeMap;
|
|
1246
1047
|
const collabNode = collabNodeMap.get(point.key);
|
|
1247
|
-
|
|
1248
1048
|
if (collabNode === undefined) {
|
|
1249
1049
|
return null;
|
|
1250
1050
|
}
|
|
1251
|
-
|
|
1252
1051
|
let offset = point.offset;
|
|
1253
1052
|
let sharedType = collabNode.getSharedType();
|
|
1254
|
-
|
|
1255
1053
|
if (collabNode instanceof CollabTextNode) {
|
|
1256
1054
|
sharedType = collabNode._parent._xmlText;
|
|
1257
1055
|
const currentOffset = collabNode.getOffset();
|
|
1258
|
-
|
|
1259
1056
|
if (currentOffset === -1) {
|
|
1260
1057
|
return null;
|
|
1261
1058
|
}
|
|
1262
|
-
|
|
1263
1059
|
offset = currentOffset + 1 + offset;
|
|
1264
1060
|
} else if (collabNode instanceof CollabElementNode && point.type === 'element') {
|
|
1265
1061
|
const parent = point.getNode();
|
|
1266
1062
|
let accumulatedOffset = 0;
|
|
1267
1063
|
let i = 0;
|
|
1268
1064
|
let node = parent.getFirstChild();
|
|
1269
|
-
|
|
1270
1065
|
while (node !== null && i++ < offset) {
|
|
1271
1066
|
if (lexical.$isTextNode(node)) {
|
|
1272
1067
|
accumulatedOffset += node.getTextContentSize() + 1;
|
|
1273
1068
|
} else {
|
|
1274
1069
|
accumulatedOffset++;
|
|
1275
1070
|
}
|
|
1276
|
-
|
|
1277
1071
|
node = node.getNextSibling();
|
|
1278
1072
|
}
|
|
1279
|
-
|
|
1280
1073
|
offset = accumulatedOffset;
|
|
1281
1074
|
}
|
|
1282
|
-
|
|
1283
1075
|
return yjs.createRelativePositionFromTypeIndex(sharedType, offset);
|
|
1284
1076
|
}
|
|
1285
|
-
|
|
1286
1077
|
function createAbsolutePosition(relativePosition, binding) {
|
|
1287
1078
|
return yjs.createAbsolutePositionFromRelativePosition(relativePosition, binding.doc);
|
|
1288
1079
|
}
|
|
1289
|
-
|
|
1290
1080
|
function shouldUpdatePosition(currentPos, pos) {
|
|
1291
1081
|
if (currentPos == null) {
|
|
1292
1082
|
if (pos != null) {
|
|
@@ -1295,10 +1085,8 @@ function shouldUpdatePosition(currentPos, pos) {
|
|
|
1295
1085
|
} else if (pos == null || !yjs.compareRelativePositions(currentPos, pos)) {
|
|
1296
1086
|
return true;
|
|
1297
1087
|
}
|
|
1298
|
-
|
|
1299
1088
|
return false;
|
|
1300
1089
|
}
|
|
1301
|
-
|
|
1302
1090
|
function createCursor(name, color) {
|
|
1303
1091
|
return {
|
|
1304
1092
|
color: color,
|
|
@@ -1306,28 +1094,22 @@ function createCursor(name, color) {
|
|
|
1306
1094
|
selection: null
|
|
1307
1095
|
};
|
|
1308
1096
|
}
|
|
1309
|
-
|
|
1310
1097
|
function destroySelection(binding, selection) {
|
|
1311
1098
|
const cursorsContainer = binding.cursorsContainer;
|
|
1312
|
-
|
|
1313
1099
|
if (cursorsContainer !== null) {
|
|
1314
1100
|
const selections = selection.selections;
|
|
1315
1101
|
const selectionsLength = selections.length;
|
|
1316
|
-
|
|
1317
1102
|
for (let i = 0; i < selectionsLength; i++) {
|
|
1318
1103
|
cursorsContainer.removeChild(selections[i]);
|
|
1319
1104
|
}
|
|
1320
1105
|
}
|
|
1321
1106
|
}
|
|
1322
|
-
|
|
1323
1107
|
function destroyCursor(binding, cursor) {
|
|
1324
1108
|
const selection = cursor.selection;
|
|
1325
|
-
|
|
1326
1109
|
if (selection !== null) {
|
|
1327
1110
|
destroySelection(binding, selection);
|
|
1328
1111
|
}
|
|
1329
1112
|
}
|
|
1330
|
-
|
|
1331
1113
|
function createCursorSelection(cursor, anchorKey, anchorOffset, focusKey, focusOffset) {
|
|
1332
1114
|
const color = cursor.color;
|
|
1333
1115
|
const caret = document.createElement('span');
|
|
@@ -1351,25 +1133,19 @@ function createCursorSelection(cursor, anchorKey, anchorOffset, focusKey, focusO
|
|
|
1351
1133
|
selections: []
|
|
1352
1134
|
};
|
|
1353
1135
|
}
|
|
1354
|
-
|
|
1355
1136
|
function updateCursor(binding, cursor, nextSelection, nodeMap) {
|
|
1356
1137
|
const editor = binding.editor;
|
|
1357
1138
|
const rootElement = editor.getRootElement();
|
|
1358
1139
|
const cursorsContainer = binding.cursorsContainer;
|
|
1359
|
-
|
|
1360
1140
|
if (cursorsContainer === null || rootElement === null) {
|
|
1361
1141
|
return;
|
|
1362
1142
|
}
|
|
1363
|
-
|
|
1364
1143
|
const cursorsContainerOffsetParent = cursorsContainer.offsetParent;
|
|
1365
|
-
|
|
1366
1144
|
if (cursorsContainerOffsetParent === null) {
|
|
1367
1145
|
return;
|
|
1368
1146
|
}
|
|
1369
|
-
|
|
1370
1147
|
const containerRect = cursorsContainerOffsetParent.getBoundingClientRect();
|
|
1371
1148
|
const prevSelection = cursor.selection;
|
|
1372
|
-
|
|
1373
1149
|
if (nextSelection === null) {
|
|
1374
1150
|
if (prevSelection === null) {
|
|
1375
1151
|
return;
|
|
@@ -1381,7 +1157,6 @@ function updateCursor(binding, cursor, nextSelection, nodeMap) {
|
|
|
1381
1157
|
} else {
|
|
1382
1158
|
cursor.selection = nextSelection;
|
|
1383
1159
|
}
|
|
1384
|
-
|
|
1385
1160
|
const caret = nextSelection.caret;
|
|
1386
1161
|
const color = nextSelection.color;
|
|
1387
1162
|
const selections = nextSelection.selections;
|
|
@@ -1391,37 +1166,31 @@ function updateCursor(binding, cursor, nextSelection, nodeMap) {
|
|
|
1391
1166
|
const focusKey = focus.key;
|
|
1392
1167
|
const anchorNode = nodeMap.get(anchorKey);
|
|
1393
1168
|
const focusNode = nodeMap.get(focusKey);
|
|
1394
|
-
|
|
1395
1169
|
if (anchorNode == null || focusNode == null) {
|
|
1396
1170
|
return;
|
|
1397
1171
|
}
|
|
1172
|
+
let selectionRects;
|
|
1398
1173
|
|
|
1399
|
-
|
|
1174
|
+
// In the case of a collapsed selection on a linebreak, we need
|
|
1400
1175
|
// to improvise as the browser will return nothing here as <br>
|
|
1401
1176
|
// apparantly take up no visual space :/
|
|
1402
1177
|
// This won't work in all cases, but it's better than just showing
|
|
1403
1178
|
// nothing all the time.
|
|
1404
|
-
|
|
1405
1179
|
if (anchorNode === focusNode && lexical.$isLineBreakNode(anchorNode)) {
|
|
1406
1180
|
const brRect = editor.getElementByKey(anchorKey).getBoundingClientRect();
|
|
1407
1181
|
selectionRects = [brRect];
|
|
1408
1182
|
} else {
|
|
1409
1183
|
const range = selection.createDOMRange(editor, anchorNode, anchor.offset, focusNode, focus.offset);
|
|
1410
|
-
|
|
1411
1184
|
if (range === null) {
|
|
1412
1185
|
return;
|
|
1413
1186
|
}
|
|
1414
|
-
|
|
1415
1187
|
selectionRects = selection.createRectsFromDOMRange(editor, range);
|
|
1416
1188
|
}
|
|
1417
|
-
|
|
1418
1189
|
const selectionsLength = selections.length;
|
|
1419
1190
|
const selectionRectsLength = selectionRects.length;
|
|
1420
|
-
|
|
1421
1191
|
for (let i = 0; i < selectionRectsLength; i++) {
|
|
1422
1192
|
const selectionRect = selectionRects[i];
|
|
1423
1193
|
let selection = selections[i];
|
|
1424
|
-
|
|
1425
1194
|
if (selection === undefined) {
|
|
1426
1195
|
selection = document.createElement('span');
|
|
1427
1196
|
selections[i] = selection;
|
|
@@ -1429,55 +1198,44 @@ function updateCursor(binding, cursor, nextSelection, nodeMap) {
|
|
|
1429
1198
|
selection.appendChild(selectionBg);
|
|
1430
1199
|
cursorsContainer.appendChild(selection);
|
|
1431
1200
|
}
|
|
1432
|
-
|
|
1433
1201
|
const top = selectionRect.top - containerRect.top;
|
|
1434
1202
|
const left = selectionRect.left - containerRect.left;
|
|
1435
1203
|
const style = `position:absolute;top:${top}px;left:${left}px;height:${selectionRect.height}px;width:${selectionRect.width}px;pointer-events:none;z-index:5;`;
|
|
1436
1204
|
selection.style.cssText = style;
|
|
1437
1205
|
selection.firstChild.style.cssText = `${style}left:0;top:0;background-color:${color};opacity:0.3;`;
|
|
1438
|
-
|
|
1439
1206
|
if (i === selectionRectsLength - 1) {
|
|
1440
1207
|
if (caret.parentNode !== selection) {
|
|
1441
1208
|
selection.appendChild(caret);
|
|
1442
1209
|
}
|
|
1443
1210
|
}
|
|
1444
1211
|
}
|
|
1445
|
-
|
|
1446
1212
|
for (let i = selectionsLength - 1; i >= selectionRectsLength; i--) {
|
|
1447
1213
|
const selection = selections[i];
|
|
1448
1214
|
cursorsContainer.removeChild(selection);
|
|
1449
1215
|
selections.pop();
|
|
1450
1216
|
}
|
|
1451
1217
|
}
|
|
1452
|
-
|
|
1453
1218
|
function syncLocalCursorPosition(binding, provider) {
|
|
1454
1219
|
const awareness = provider.awareness;
|
|
1455
1220
|
const localState = awareness.getLocalState();
|
|
1456
|
-
|
|
1457
1221
|
if (localState === null) {
|
|
1458
1222
|
return;
|
|
1459
1223
|
}
|
|
1460
|
-
|
|
1461
1224
|
const anchorPos = localState.anchorPos;
|
|
1462
1225
|
const focusPos = localState.focusPos;
|
|
1463
|
-
|
|
1464
1226
|
if (anchorPos !== null && focusPos !== null) {
|
|
1465
1227
|
const anchorAbsPos = createAbsolutePosition(anchorPos, binding);
|
|
1466
1228
|
const focusAbsPos = createAbsolutePosition(focusPos, binding);
|
|
1467
|
-
|
|
1468
1229
|
if (anchorAbsPos !== null && focusAbsPos !== null) {
|
|
1469
1230
|
const [anchorCollabNode, anchorOffset] = getCollabNodeAndOffset(anchorAbsPos.type, anchorAbsPos.index);
|
|
1470
1231
|
const [focusCollabNode, focusOffset] = getCollabNodeAndOffset(focusAbsPos.type, focusAbsPos.index);
|
|
1471
|
-
|
|
1472
1232
|
if (anchorCollabNode !== null && focusCollabNode !== null) {
|
|
1473
1233
|
const anchorKey = anchorCollabNode.getKey();
|
|
1474
1234
|
const focusKey = focusCollabNode.getKey();
|
|
1475
1235
|
const selection = lexical.$getSelection();
|
|
1476
|
-
|
|
1477
1236
|
if (!lexical.$isRangeSelection(selection)) {
|
|
1478
1237
|
return;
|
|
1479
1238
|
}
|
|
1480
|
-
|
|
1481
1239
|
const anchor = selection.anchor;
|
|
1482
1240
|
const focus = selection.focus;
|
|
1483
1241
|
setPoint(anchor, anchorKey, anchorOffset);
|
|
@@ -1486,46 +1244,38 @@ function syncLocalCursorPosition(binding, provider) {
|
|
|
1486
1244
|
}
|
|
1487
1245
|
}
|
|
1488
1246
|
}
|
|
1489
|
-
|
|
1490
1247
|
function setPoint(point, key, offset) {
|
|
1491
1248
|
if (point.key !== key || point.offset !== offset) {
|
|
1492
1249
|
let anchorNode = lexical.$getNodeByKey(key);
|
|
1493
|
-
|
|
1494
1250
|
if (anchorNode !== null && !lexical.$isElementNode(anchorNode) && !lexical.$isTextNode(anchorNode)) {
|
|
1495
1251
|
const parent = anchorNode.getParentOrThrow();
|
|
1496
1252
|
key = parent.getKey();
|
|
1497
1253
|
offset = anchorNode.getIndexWithinParent();
|
|
1498
1254
|
anchorNode = parent;
|
|
1499
1255
|
}
|
|
1500
|
-
|
|
1501
1256
|
point.set(key, offset, lexical.$isElementNode(anchorNode) ? 'element' : 'text');
|
|
1502
1257
|
}
|
|
1503
1258
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1259
|
+
function getCollabNodeAndOffset(
|
|
1260
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1506
1261
|
sharedType, offset) {
|
|
1507
1262
|
const collabNode = sharedType._collabNode;
|
|
1508
|
-
|
|
1509
1263
|
if (collabNode === undefined) {
|
|
1510
1264
|
return [null, 0];
|
|
1511
1265
|
}
|
|
1512
|
-
|
|
1513
1266
|
if (collabNode instanceof CollabElementNode) {
|
|
1514
1267
|
const {
|
|
1515
1268
|
node,
|
|
1516
1269
|
offset: collabNodeOffset
|
|
1517
1270
|
} = getPositionFromElementAndOffset(collabNode, offset, true);
|
|
1518
|
-
|
|
1519
1271
|
if (node === null) {
|
|
1520
1272
|
return [collabNode, 0];
|
|
1521
1273
|
} else {
|
|
1522
1274
|
return [node, collabNodeOffset];
|
|
1523
1275
|
}
|
|
1524
1276
|
}
|
|
1525
|
-
|
|
1526
1277
|
return [null, 0];
|
|
1527
1278
|
}
|
|
1528
|
-
|
|
1529
1279
|
function syncCursorPositions(binding, provider) {
|
|
1530
1280
|
const awarenessStates = Array.from(provider.awareness.getStates());
|
|
1531
1281
|
const localClientID = binding.clientID;
|
|
@@ -1533,11 +1283,9 @@ function syncCursorPositions(binding, provider) {
|
|
|
1533
1283
|
const editor = binding.editor;
|
|
1534
1284
|
const nodeMap = editor._editorState._nodeMap;
|
|
1535
1285
|
const visitedClientIDs = new Set();
|
|
1536
|
-
|
|
1537
1286
|
for (let i = 0; i < awarenessStates.length; i++) {
|
|
1538
1287
|
const awarenessState = awarenessStates[i];
|
|
1539
1288
|
const [clientID, awareness] = awarenessState;
|
|
1540
|
-
|
|
1541
1289
|
if (clientID !== localClientID) {
|
|
1542
1290
|
visitedClientIDs.add(clientID);
|
|
1543
1291
|
const {
|
|
@@ -1549,25 +1297,20 @@ function syncCursorPositions(binding, provider) {
|
|
|
1549
1297
|
} = awareness;
|
|
1550
1298
|
let selection = null;
|
|
1551
1299
|
let cursor = cursors.get(clientID);
|
|
1552
|
-
|
|
1553
1300
|
if (cursor === undefined) {
|
|
1554
1301
|
cursor = createCursor(name, color);
|
|
1555
1302
|
cursors.set(clientID, cursor);
|
|
1556
1303
|
}
|
|
1557
|
-
|
|
1558
1304
|
if (anchorPos !== null && focusPos !== null && focusing) {
|
|
1559
1305
|
const anchorAbsPos = createAbsolutePosition(anchorPos, binding);
|
|
1560
1306
|
const focusAbsPos = createAbsolutePosition(focusPos, binding);
|
|
1561
|
-
|
|
1562
1307
|
if (anchorAbsPos !== null && focusAbsPos !== null) {
|
|
1563
1308
|
const [anchorCollabNode, anchorOffset] = getCollabNodeAndOffset(anchorAbsPos.type, anchorAbsPos.index);
|
|
1564
1309
|
const [focusCollabNode, focusOffset] = getCollabNodeAndOffset(focusAbsPos.type, focusAbsPos.index);
|
|
1565
|
-
|
|
1566
1310
|
if (anchorCollabNode !== null && focusCollabNode !== null) {
|
|
1567
1311
|
const anchorKey = anchorCollabNode.getKey();
|
|
1568
1312
|
const focusKey = focusCollabNode.getKey();
|
|
1569
1313
|
selection = cursor.selection;
|
|
1570
|
-
|
|
1571
1314
|
if (selection === null) {
|
|
1572
1315
|
selection = createCursorSelection(cursor, anchorKey, anchorOffset, focusKey, focusOffset);
|
|
1573
1316
|
} else {
|
|
@@ -1581,19 +1324,14 @@ function syncCursorPositions(binding, provider) {
|
|
|
1581
1324
|
}
|
|
1582
1325
|
}
|
|
1583
1326
|
}
|
|
1584
|
-
|
|
1585
1327
|
updateCursor(binding, cursor, selection, nodeMap);
|
|
1586
1328
|
}
|
|
1587
1329
|
}
|
|
1588
|
-
|
|
1589
1330
|
const allClientIDs = Array.from(cursors.keys());
|
|
1590
|
-
|
|
1591
1331
|
for (let i = 0; i < allClientIDs.length; i++) {
|
|
1592
1332
|
const clientID = allClientIDs[i];
|
|
1593
|
-
|
|
1594
1333
|
if (!visitedClientIDs.has(clientID)) {
|
|
1595
1334
|
const cursor = cursors.get(clientID);
|
|
1596
|
-
|
|
1597
1335
|
if (cursor !== undefined) {
|
|
1598
1336
|
destroyCursor(binding, cursor);
|
|
1599
1337
|
cursors.delete(clientID);
|
|
@@ -1604,11 +1342,9 @@ function syncCursorPositions(binding, provider) {
|
|
|
1604
1342
|
function syncLexicalSelectionToYjs(binding, provider, prevSelection, nextSelection) {
|
|
1605
1343
|
const awareness = provider.awareness;
|
|
1606
1344
|
const localState = awareness.getLocalState();
|
|
1607
|
-
|
|
1608
1345
|
if (localState === null) {
|
|
1609
1346
|
return;
|
|
1610
1347
|
}
|
|
1611
|
-
|
|
1612
1348
|
const {
|
|
1613
1349
|
anchorPos: currentAnchorPos,
|
|
1614
1350
|
focusPos: currentFocusPos,
|
|
@@ -1619,18 +1355,15 @@ function syncLexicalSelectionToYjs(binding, provider, prevSelection, nextSelecti
|
|
|
1619
1355
|
} = localState;
|
|
1620
1356
|
let anchorPos = null;
|
|
1621
1357
|
let focusPos = null;
|
|
1622
|
-
|
|
1623
1358
|
if (nextSelection === null || currentAnchorPos !== null && !nextSelection.is(prevSelection)) {
|
|
1624
1359
|
if (prevSelection === null) {
|
|
1625
1360
|
return;
|
|
1626
1361
|
}
|
|
1627
1362
|
}
|
|
1628
|
-
|
|
1629
1363
|
if (lexical.$isRangeSelection(nextSelection)) {
|
|
1630
1364
|
anchorPos = createRelativePosition(nextSelection.anchor, binding);
|
|
1631
1365
|
focusPos = createRelativePosition(nextSelection.focus, binding);
|
|
1632
1366
|
}
|
|
1633
|
-
|
|
1634
1367
|
if (shouldUpdatePosition(currentAnchorPos, anchorPos) || shouldUpdatePosition(currentFocusPos, focusPos)) {
|
|
1635
1368
|
awareness.setLocalState({
|
|
1636
1369
|
anchorPos,
|
|
@@ -1651,24 +1384,24 @@ function syncLexicalSelectionToYjs(binding, provider, prevSelection, nextSelecti
|
|
|
1651
1384
|
*
|
|
1652
1385
|
*/
|
|
1653
1386
|
|
|
1387
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1654
1388
|
function syncEvent(binding, event) {
|
|
1655
1389
|
const {
|
|
1656
1390
|
target
|
|
1657
1391
|
} = event;
|
|
1658
1392
|
const collabNode = getOrInitCollabNodeFromSharedType(binding, target);
|
|
1659
|
-
|
|
1660
1393
|
if (collabNode instanceof CollabElementNode && event instanceof yjs.YTextEvent) {
|
|
1661
1394
|
// @ts-expect-error We need to access the private property of the class
|
|
1662
1395
|
const {
|
|
1663
1396
|
keysChanged,
|
|
1664
1397
|
childListChanged,
|
|
1665
1398
|
delta
|
|
1666
|
-
} = event;
|
|
1399
|
+
} = event;
|
|
1667
1400
|
|
|
1401
|
+
// Update
|
|
1668
1402
|
if (keysChanged.size > 0) {
|
|
1669
1403
|
collabNode.syncPropertiesFromYjs(binding, keysChanged);
|
|
1670
1404
|
}
|
|
1671
|
-
|
|
1672
1405
|
if (childListChanged) {
|
|
1673
1406
|
collabNode.applyChildrenYjsDelta(binding, delta);
|
|
1674
1407
|
collabNode.syncChildrenFromYjs(binding);
|
|
@@ -1676,16 +1409,18 @@ function syncEvent(binding, event) {
|
|
|
1676
1409
|
} else if (collabNode instanceof CollabTextNode && event instanceof yjs.YMapEvent) {
|
|
1677
1410
|
const {
|
|
1678
1411
|
keysChanged
|
|
1679
|
-
} = event;
|
|
1412
|
+
} = event;
|
|
1680
1413
|
|
|
1414
|
+
// Update
|
|
1681
1415
|
if (keysChanged.size > 0) {
|
|
1682
1416
|
collabNode.syncPropertiesAndTextFromYjs(binding, keysChanged);
|
|
1683
1417
|
}
|
|
1684
1418
|
} else if (collabNode instanceof CollabDecoratorNode && event instanceof yjs.YXmlEvent) {
|
|
1685
1419
|
const {
|
|
1686
1420
|
attributesChanged
|
|
1687
|
-
} = event;
|
|
1421
|
+
} = event;
|
|
1688
1422
|
|
|
1423
|
+
// Update
|
|
1689
1424
|
if (attributesChanged.size > 0) {
|
|
1690
1425
|
collabNode.syncPropertiesFromYjs(binding, attributesChanged);
|
|
1691
1426
|
}
|
|
@@ -1695,59 +1430,53 @@ function syncEvent(binding, event) {
|
|
|
1695
1430
|
}
|
|
1696
1431
|
}
|
|
1697
1432
|
}
|
|
1698
|
-
|
|
1699
1433
|
function syncYjsChangesToLexical(binding, provider, events, isFromUndoManger) {
|
|
1700
1434
|
const editor = binding.editor;
|
|
1701
|
-
const currentEditorState = editor._editorState;
|
|
1435
|
+
const currentEditorState = editor._editorState;
|
|
1436
|
+
|
|
1437
|
+
// This line precompute the delta before editor update. The reason is
|
|
1702
1438
|
// delta is computed when it is accessed. Note that this can only be
|
|
1703
1439
|
// safely computed during the event call. If it is accessed after event
|
|
1704
1440
|
// call it might result in unexpected behavior.
|
|
1705
1441
|
// https://github.com/yjs/yjs/blob/00ef472d68545cb260abd35c2de4b3b78719c9e4/src/utils/YEvent.js#L132
|
|
1706
|
-
|
|
1707
1442
|
events.forEach(event => event.delta);
|
|
1708
1443
|
editor.update(() => {
|
|
1709
1444
|
const pendingEditorState = editor._pendingEditorState;
|
|
1710
|
-
|
|
1711
1445
|
for (let i = 0; i < events.length; i++) {
|
|
1712
1446
|
const event = events[i];
|
|
1713
1447
|
syncEvent(binding, event);
|
|
1714
1448
|
}
|
|
1715
|
-
|
|
1716
1449
|
const selection = lexical.$getSelection();
|
|
1717
|
-
|
|
1718
1450
|
if (lexical.$isRangeSelection(selection)) {
|
|
1719
1451
|
// We can't use Yjs's cursor position here, as it doesn't always
|
|
1720
1452
|
// handle selection recovery correctly – especially on elements that
|
|
1721
1453
|
// get moved around or split. So instead, we roll our own solution.
|
|
1722
1454
|
if (doesSelectionNeedRecovering(selection)) {
|
|
1723
1455
|
const prevSelection = currentEditorState._selection;
|
|
1724
|
-
|
|
1725
1456
|
if (lexical.$isRangeSelection(prevSelection)) {
|
|
1726
1457
|
const prevOffsetView = offset.$createOffsetView(editor, 0, currentEditorState);
|
|
1727
1458
|
const nextOffsetView = offset.$createOffsetView(editor, 0, pendingEditorState);
|
|
1728
1459
|
const [start, end] = prevOffsetView.getOffsetsFromSelection(prevSelection);
|
|
1729
1460
|
const nextSelection = start >= 0 && end >= 0 ? nextOffsetView.createSelectionFromOffsets(start, end, prevOffsetView) : null;
|
|
1730
|
-
|
|
1731
1461
|
if (nextSelection !== null) {
|
|
1732
1462
|
lexical.$setSelection(nextSelection);
|
|
1733
1463
|
} else {
|
|
1734
1464
|
// Fallback is to use the Yjs cursor position
|
|
1735
1465
|
syncLocalCursorPosition(binding, provider);
|
|
1736
|
-
|
|
1737
1466
|
if (doesSelectionNeedRecovering(selection)) {
|
|
1738
|
-
const root = lexical.$getRoot();
|
|
1739
|
-
// we need to re-add a paragraph
|
|
1467
|
+
const root = lexical.$getRoot();
|
|
1740
1468
|
|
|
1469
|
+
// If there was a collision on the top level paragraph
|
|
1470
|
+
// we need to re-add a paragraph
|
|
1741
1471
|
if (root.getChildrenSize() === 0) {
|
|
1742
1472
|
root.append(lexical.$createParagraphNode());
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1473
|
+
}
|
|
1745
1474
|
|
|
1475
|
+
// Fallback
|
|
1746
1476
|
lexical.$getRoot().selectEnd();
|
|
1747
1477
|
}
|
|
1748
1478
|
}
|
|
1749
1479
|
}
|
|
1750
|
-
|
|
1751
1480
|
syncLexicalSelectionToYjs(binding, provider, prevSelection, lexical.$getSelection());
|
|
1752
1481
|
} else {
|
|
1753
1482
|
syncLocalCursorPosition(binding, provider);
|
|
@@ -1761,18 +1490,15 @@ function syncYjsChangesToLexical(binding, provider, events, isFromUndoManger) {
|
|
|
1761
1490
|
tag: isFromUndoManger ? 'historic' : 'collaboration'
|
|
1762
1491
|
});
|
|
1763
1492
|
}
|
|
1764
|
-
|
|
1765
1493
|
function handleNormalizationMergeConflicts(binding, normalizedNodes) {
|
|
1766
1494
|
// We handle the merge operations here
|
|
1767
1495
|
const normalizedNodesKeys = Array.from(normalizedNodes);
|
|
1768
1496
|
const collabNodeMap = binding.collabNodeMap;
|
|
1769
1497
|
const mergedNodes = [];
|
|
1770
|
-
|
|
1771
1498
|
for (let i = 0; i < normalizedNodesKeys.length; i++) {
|
|
1772
1499
|
const nodeKey = normalizedNodesKeys[i];
|
|
1773
1500
|
const lexicalNode = lexical.$getNodeByKey(nodeKey);
|
|
1774
1501
|
const collabNode = collabNodeMap.get(nodeKey);
|
|
1775
|
-
|
|
1776
1502
|
if (collabNode instanceof CollabTextNode) {
|
|
1777
1503
|
if (lexical.$isTextNode(lexicalNode)) {
|
|
1778
1504
|
// We mutate the text collab nodes after removing
|
|
@@ -1780,16 +1506,12 @@ function handleNormalizationMergeConflicts(binding, normalizedNodes) {
|
|
|
1780
1506
|
mergedNodes.push([collabNode, lexicalNode.__text]);
|
|
1781
1507
|
} else {
|
|
1782
1508
|
const offset = collabNode.getOffset();
|
|
1783
|
-
|
|
1784
1509
|
if (offset === -1) {
|
|
1785
1510
|
continue;
|
|
1786
1511
|
}
|
|
1787
|
-
|
|
1788
1512
|
const parent = collabNode._parent;
|
|
1789
1513
|
collabNode._normalized = true;
|
|
1790
|
-
|
|
1791
1514
|
parent._xmlText.delete(offset, 1);
|
|
1792
|
-
|
|
1793
1515
|
collabNodeMap.delete(nodeKey);
|
|
1794
1516
|
const parentChildren = parent._children;
|
|
1795
1517
|
const index = parentChildren.indexOf(collabNode);
|
|
@@ -1797,16 +1519,13 @@ function handleNormalizationMergeConflicts(binding, normalizedNodes) {
|
|
|
1797
1519
|
}
|
|
1798
1520
|
}
|
|
1799
1521
|
}
|
|
1800
|
-
|
|
1801
1522
|
for (let i = 0; i < mergedNodes.length; i++) {
|
|
1802
1523
|
const [collabNode, text] = mergedNodes[i];
|
|
1803
|
-
|
|
1804
1524
|
if (collabNode instanceof CollabTextNode && typeof text === 'string') {
|
|
1805
1525
|
collabNode._text = text;
|
|
1806
1526
|
}
|
|
1807
1527
|
}
|
|
1808
1528
|
}
|
|
1809
|
-
|
|
1810
1529
|
function syncLexicalUpdateToYjs(binding, provider, prevEditorState, currEditorState, dirtyElements, dirtyLeaves, normalizedNodes, tags) {
|
|
1811
1530
|
syncWithTransaction(binding, () => {
|
|
1812
1531
|
currEditorState.read(() => {
|
|
@@ -1821,10 +1540,8 @@ function syncLexicalUpdateToYjs(binding, provider, prevEditorState, currEditorSt
|
|
|
1821
1540
|
if (normalizedNodes.size > 0) {
|
|
1822
1541
|
handleNormalizationMergeConflicts(binding, normalizedNodes);
|
|
1823
1542
|
}
|
|
1824
|
-
|
|
1825
1543
|
return;
|
|
1826
1544
|
}
|
|
1827
|
-
|
|
1828
1545
|
if (dirtyElements.has('root')) {
|
|
1829
1546
|
const prevNodeMap = prevEditorState._nodeMap;
|
|
1830
1547
|
const nextLexicalRoot = lexical.$getRoot();
|
|
@@ -1832,7 +1549,6 @@ function syncLexicalUpdateToYjs(binding, provider, prevEditorState, currEditorSt
|
|
|
1832
1549
|
collabRoot.syncPropertiesFromLexical(binding, nextLexicalRoot, prevNodeMap);
|
|
1833
1550
|
collabRoot.syncChildrenFromLexical(binding, nextLexicalRoot, prevNodeMap, dirtyElements, dirtyLeaves);
|
|
1834
1551
|
}
|
|
1835
|
-
|
|
1836
1552
|
const selection = lexical.$getSelection();
|
|
1837
1553
|
const prevSelection = prevEditorState._selection;
|
|
1838
1554
|
syncLexicalSelectionToYjs(binding, provider, prevSelection, selection);
|
|
@@ -1863,7 +1579,6 @@ function setLocalStateFocus(provider, name, color, focusing, awarenessData) {
|
|
|
1863
1579
|
awareness
|
|
1864
1580
|
} = provider;
|
|
1865
1581
|
let localState = awareness.getLocalState();
|
|
1866
|
-
|
|
1867
1582
|
if (localState === null) {
|
|
1868
1583
|
localState = {
|
|
1869
1584
|
anchorPos: null,
|
|
@@ -1874,7 +1589,6 @@ function setLocalStateFocus(provider, name, color, focusing, awarenessData) {
|
|
|
1874
1589
|
name
|
|
1875
1590
|
};
|
|
1876
1591
|
}
|
|
1877
|
-
|
|
1878
1592
|
localState.focusing = focusing;
|
|
1879
1593
|
awareness.setLocalState(localState);
|
|
1880
1594
|
}
|