import { ISegment } from "@fluidframework/merge-tree" ; export class SegmentSpan { public get segments ( ) : readonly ISegment [ ] { return this . _segments ; } public get startOffset ( ) { return this . _startOffset ; } public get startPosition ( ) { return this . firstPosition + this . _startOffset ; } public get endPosition ( ) { return this . lastPosition + Math . min ( this . _endOffset , this . lastSegment . cachedLength ) ; } public get isEmpty ( ) { return isNaN ( this . firstPosition ) ; } public get firstSegment ( ) { return this . segments [ 0 ] ; } public get lastSegment ( ) { return this . segments [ this . segments . length - 1 ] ; } public get length ( ) { return this . endPosition - this . startPosition ; } private firstPosition = NaN ; private lastPosition = NaN ; private readonly _segments = [ ] ; private _endOffset = NaN ; private _startOffset = NaN ; constructor ( position ? , segment ? , startOffset ? , endOffset ? ) { if ( position !== undefined ) { this . append ( position , segment , startOffset , endOffset ) ; } } public forEach ( callback ) { let startOffset = this . _startOffset ; let position = this . firstPosition ; const final = this . endPosition ; for ( const segment of this . segments ) { if ( callback ( position , segment , startOffset , Math . min ( segment . cachedLength , final - position ) ) === false ) { return ; } position += segment . cachedLength ; startOffset = 0 ; } } public append ( position , segment , startOffset , endOffset ) { this . _segments . push ( segment ) ; this . lastPosition = position ; this . _endOffset = endOffset ; if ( this . isEmpty ) { this . firstPosition = position ; this . _startOffset = Math . max ( startOffset , 0 ) ; } console . assert ( 0 <= this . _startOffset && this . _startOffset <= segment . cachedLength ) ; console . assert ( 0 <= this . startPosition && this . startPosition <= this . endPosition ) ; } /**
     * Given an offset from the beginning of the span, returns the segment that contains the offset
     * as well as the offset from the segment start.
     */ public spanOffsetToSegmentOffset ( spanOffset ) { let currentSpanOffset = spanOffset ; let segment ; let offset = NaN ; this . forEach ( ( position , candidate , startOffset , endOffset ) => { segment = candidate ; const len = endOffset - startOffset ; offset = startOffset + currentSpanOffset ; if ( currentSpanOffset < len ) { return false ; } currentSpanOffset -= len ; return true ; } ) ; return { segment , offset } ; } }