import { BaseSegment , IJSONSegment , ISegment , PropertySet , LocalReferenceCollection , } from "@fluidframework/merge-tree" ; import { IChannelAttributes , IFluidDataStoreRuntime , Serializable } from "@fluidframework/datastore-definitions" ; import { SharedSegmentSequence } from "./sequence" ; const MaxRun = 128 ; export interface IJSONRunSegment < T > extends IJSONSegment { items : Serializable < T > [ ] ; } export class SubSequence < T > extends BaseSegment { public static readonly typeString : string = "SubSequence" ; public static is ( segment ) { return segment . type === SubSequence . typeString ; } public static fromJSONObject < U > ( spec ) { if ( spec && typeof spec === "object" && "items" in spec ) { const segment = new SubSequence < U > ( spec . items ) ; if ( spec . props ) { segment . addProperties ( spec . props ) ; } return segment ; } return undefined ; } public readonly type = SubSequence . typeString ; constructor ( public items ) { super ( ) ; this . cachedLength = items . length ; } public toJSONObject ( ) { const obj = { items : this . items } ; super . addSerializedProps ( obj ) ; return obj ; } public clone ( start = 0 , end ? ) { const clonedItems = this . items . slice ( start , end ) ; const b = new SubSequence ( clonedItems ) ; this . cloneInto ( b ) ; return b ; } public canAppend ( segment ) { return SubSequence . is ( segment ) && ( this . cachedLength <= MaxRun || segment . cachedLength <= MaxRun ) ; } public toString ( ) { return this . items . toString ( ) ; } public append ( segment ) { if ( ! SubSequence . is ( segment ) ) { throw new Error ( "can only append another run segment" ) ; } LocalReferenceCollection . append ( this , segment ) ; this . items = this . items . concat ( segment . items ) ; this . cachedLength = this . items . length ; } public removeRange ( start , end ) { let remnantItems = [ ] ; const len = this . items . length ; if ( start > 0 ) { remnantItems = remnantItems . concat ( this . items . slice ( 0 , start ) ) ; } if ( end < len ) { remnantItems = remnantItems . concat ( this . items . slice ( end ) ) ; } this . items = remnantItems ; this . cachedLength = this . items . length ; return ( this . items . length === 0 ) ; } protected createSplitSegmentAt ( pos ) { if ( pos > 0 ) { const remainingItems = this . items . slice ( pos ) ; this . items = this . items . slice ( 0 , pos ) ; this . cachedLength = this . items . length ; const leafSegment = new SubSequence ( remainingItems ) ; return leafSegment ; } } } export class SharedSequence < T > extends SharedSegmentSequence < SubSequence < T > > { constructor ( document , public id , attributes , specToSegment ) { super ( document , id , attributes , specToSegment ) ; } param pos param items param props public insert ( pos , items , props ? ) { const segment = new SubSequence < T > ( items ) ; if ( props ) { segment . addProperties ( props ) ; } const insertOp = this . client . insertSegmentLocal ( pos , segment ) ; if ( insertOp ) { this . submitSequenceMessage ( insertOp ) ; } } param start param end public remove ( start , end ) { this . removeRange ( start , end ) ; } /**
     * Returns the total count of items in the sequence
     */ public getItemCount ( ) { return this . getLength ( ) ; } param start param end public getItems ( start , end ? ) { const items = [ ] ; let firstSegment ; if ( end !== undefined && end <= start ) { return items ; } this . walkSegments ( ( segment ) => { if ( SubSequence . is ( segment ) ) { if ( firstSegment === undefined ) { firstSegment = segment ; } items . push ( ... segment . items ) ; } return true ; } , start , end ) ; if ( firstSegment !== undefined ) { items . splice ( 0 , start - this . getPosition ( firstSegment ) ) ; } if ( end !== undefined ) { items . splice ( end - start ) ; } return items ; } }