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