1
1
const assert = require ( 'assert' )
2
2
const DocumentTree = require ( './document-tree' )
3
+ const SplitTree = require ( './split-tree' )
3
4
4
5
module . exports =
5
6
class DocumentReplica {
6
7
constructor ( siteId ) {
7
8
assert ( siteId !== 0 , 'siteId 0 is reserved' )
8
9
this . siteId = siteId
9
10
this . nextSequenceNumber = 0
10
- this . insertionStartsByOpId = new Map ( )
11
+ this . splitTreesByOpId = new Map ( )
11
12
this . deletedOffsetRangesByOpId = new Map ( )
12
13
this . undoCountsByOpId = new Map ( )
13
14
14
15
const firstSegment = { opId : { site : 0 , seq : 0 } , offset : 0 , pos : 0 , text : '' , nextSplit : null , deletions : new Set ( ) }
15
- this . insertionStartsByOpId . set ( opIdToString ( firstSegment . opId ) , firstSegment )
16
+ this . splitTreesByOpId . set ( opIdToString ( firstSegment . opId ) , new SplitTree ( firstSegment ) )
16
17
17
18
const lastSegment = { opId : { site : 0 , seq : 1 } , offset : 0 , pos : 1 , text : '' , nextSplit : null , deletions : new Set ( ) }
18
- this . insertionStartsByOpId . set ( opIdToString ( lastSegment . opId ) , lastSegment )
19
+ this . splitTreesByOpId . set ( opIdToString ( lastSegment . opId ) , new SplitTree ( lastSegment ) )
19
20
20
21
this . documentTree = new DocumentTree (
21
22
firstSegment ,
@@ -48,7 +49,7 @@ class DocumentReplica {
48
49
deletions : new Set ( )
49
50
}
50
51
this . documentTree . insertBetween ( left , right , newSegment )
51
- this . insertionStartsByOpId . set ( opIdToString ( opId ) , newSegment )
52
+ this . splitTreesByOpId . set ( opIdToString ( opId ) , new SplitTree ( newSegment ) )
52
53
53
54
return {
54
55
type : 'insert' ,
@@ -83,7 +84,7 @@ class DocumentReplica {
83
84
84
85
segment . deletions . add ( opIdString )
85
86
this . documentTree . splayNode ( segment )
86
- this . documentTree . updateDocumentSubtreeExtent ( segment )
87
+ this . documentTree . updateSubtreeExtent ( segment )
87
88
if ( segment === right ) break
88
89
segment = this . documentTree . getSuccessor ( segment )
89
90
}
@@ -107,9 +108,9 @@ class DocumentReplica {
107
108
if ( newUndoCount <= undoCount ) return [ ]
108
109
109
110
let segmentRangesToUpdate
110
- let insertionStartSegment = this . insertionStartsByOpId . get ( opIdString )
111
- if ( insertionStartSegment ) {
112
- segmentRangesToUpdate = [ { startSegment : insertionStartSegment , endSegment : null } ]
111
+ let insertionSplitTree = this . splitTreesByOpId . get ( opIdString )
112
+ if ( insertionSplitTree ) {
113
+ segmentRangesToUpdate = [ { startSegment : insertionSplitTree . getStart ( ) , endSegment : null } ]
113
114
} else {
114
115
segmentRangesToUpdate = [ ]
115
116
const deletedOffsetRanges = this . deletedOffsetRangesByOpId . get ( opIdString )
@@ -160,7 +161,7 @@ class DocumentReplica {
160
161
this . undoCountsByOpId . set ( opIdString , newUndoCount )
161
162
for ( let i = segmentsToUpdate . length - 1 ; i >= 0 ; i -- ) {
162
163
this . documentTree . splayNode ( segmentsToUpdate [ i ] )
163
- this . documentTree . updateDocumentSubtreeExtent ( segmentsToUpdate [ i ] )
164
+ this . documentTree . updateSubtreeExtent ( segmentsToUpdate [ i ] )
164
165
}
165
166
166
167
return opsToApply . sort ( ( a , b ) => a . pos - b . pos )
@@ -170,21 +171,21 @@ class DocumentReplica {
170
171
switch ( op . type ) {
171
172
case 'insert' :
172
173
return (
173
- this . insertionStartsByOpId . has ( opIdToString ( op . leftDependencyId ) ) &&
174
- this . insertionStartsByOpId . has ( opIdToString ( op . rightDependencyId ) )
174
+ this . splitTreesByOpId . has ( opIdToString ( op . leftDependencyId ) ) &&
175
+ this . splitTreesByOpId . has ( opIdToString ( op . rightDependencyId ) )
175
176
)
176
177
case 'delete' :
177
178
for ( let i = 0 ; i < op . offsetRanges . length ; i ++ ) {
178
179
const insertionIdString = opIdToString ( op . offsetRanges [ i ] . opId )
179
- if ( ! this . insertionStartsByOpId . has ( insertionIdString ) ) {
180
+ if ( ! this . splitTreesByOpId . has ( insertionIdString ) ) {
180
181
return false
181
182
}
182
183
}
183
184
return true
184
185
case 'undo' :
185
186
const opIdString = opIdToString ( op . opId )
186
187
return (
187
- this . insertionStartsByOpId . has ( opIdString ) ||
188
+ this . splitTreesByOpId . has ( opIdString ) ||
188
189
this . deletedOffsetRangesByOpId . has ( opIdString )
189
190
)
190
191
default :
@@ -236,7 +237,7 @@ class DocumentReplica {
236
237
deletions : new Set ( )
237
238
}
238
239
this . documentTree . insertBetween ( leftDependency , rightDependency , newSegment )
239
- this . insertionStartsByOpId . set ( opIdToString ( opId ) , newSegment )
240
+ this . splitTreesByOpId . set ( opIdToString ( opId ) , new SplitTree ( newSegment ) )
240
241
241
242
if ( this . isSegmentVisible ( newSegment ) ) {
242
243
return [ {
@@ -290,7 +291,7 @@ class DocumentReplica {
290
291
while ( true ) {
291
292
node . deletions . add ( deletionOpIdString )
292
293
this . documentTree . splayNode ( node )
293
- this . documentTree . updateDocumentSubtreeExtent ( node )
294
+ this . documentTree . updateSubtreeExtent ( node )
294
295
if ( node === right ) break
295
296
node = node . nextSplit
296
297
}
@@ -306,54 +307,43 @@ class DocumentReplica {
306
307
findLocalSegmentBoundary ( position ) {
307
308
const { segment, start, end} = this . documentTree . findSegmentContainingPosition ( position )
308
309
if ( position < end ) {
309
- return this . splitSegment ( segment , position - start )
310
+ const splitTree = this . splitTreesByOpId . get ( opIdToString ( segment . opId ) )
311
+ return this . splitSegment ( splitTree , segment , position - start )
310
312
} else {
311
313
return [ segment , this . documentTree . getSuccessor ( segment ) ]
312
314
}
313
315
}
314
316
315
- splitSegment ( segment , offset ) {
316
- const suffix = Object . assign ( { } , segment )
317
- suffix . text = segment . text . slice ( offset )
318
- suffix . opId = Object . assign ( { } , segment . opId )
319
- suffix . offset += offset
317
+ splitSegment ( splitTree , segment , offset ) {
318
+ const suffix = splitTree . splitSegment ( segment , offset )
320
319
suffix . pos = ( segment . pos + this . documentTree . getSuccessor ( segment ) . pos ) / 2
321
- suffix . deletions = new Set ( suffix . deletions )
322
- segment . text = segment . text . slice ( 0 , offset )
323
- segment . nextSplit = suffix
324
320
this . documentTree . splitSegment ( segment , suffix )
325
321
return [ segment , suffix ]
326
322
}
327
323
328
324
findSegmentStart ( opId , offset ) {
329
- let segment = this . insertionStartsByOpId . get ( opIdToString ( opId ) )
330
- while ( segment ) {
331
- const segmentEndOffset = segment . offset + segment . text . length
332
- if ( segment . offset === offset ) {
333
- return segment
334
- } else if ( segmentEndOffset > offset ) {
335
- assert ( segment . offset < offset )
336
- const [ prefix , suffix ] = this . splitSegment ( segment , offset - segment . offset )
337
- return suffix
338
- }
339
-
340
- segment = segment . nextSplit
325
+ const splitTree = this . splitTreesByOpId . get ( opIdToString ( opId ) )
326
+ const segment = splitTree . findSegmentContainingOffset ( offset )
327
+ const segmentEndOffset = segment . offset + segment . text . length
328
+ if ( segment . offset === offset ) {
329
+ return segment
330
+ } else if ( segmentEndOffset === offset ) {
331
+ return segment . nextSplit
332
+ } else {
333
+ const [ prefix , suffix ] = this . splitSegment ( splitTree , segment , offset - segment . offset )
334
+ return suffix
341
335
}
342
336
}
343
337
344
338
findSegmentEnd ( opId , offset ) {
345
- let segment = this . insertionStartsByOpId . get ( opIdToString ( opId ) )
346
- while ( segment ) {
347
- const segmentEndOffset = segment . offset + segment . text . length
348
- if ( segmentEndOffset === offset ) {
349
- return segment
350
- } else if ( segmentEndOffset > offset ) {
351
- assert ( segment . offset < offset )
352
- const [ prefix , suffix ] = this . splitSegment ( segment , offset - segment . offset )
353
- return prefix
354
- }
355
-
356
- segment = segment . nextSplit
339
+ const splitTree = this . splitTreesByOpId . get ( opIdToString ( opId ) )
340
+ const segment = splitTree . findSegmentContainingOffset ( offset )
341
+ const segmentEndOffset = segment . offset + segment . text . length
342
+ if ( segmentEndOffset === offset ) {
343
+ return segment
344
+ } else {
345
+ const [ prefix , suffix ] = this . splitSegment ( splitTree , segment , offset - segment . offset )
346
+ return prefix
357
347
}
358
348
}
359
349
0 commit comments