/**
 * Contains common items.
 */ import { Color , PageContent , RedBlackTree } from "../pageModel" ; import { SENTINEL_INDEX , nextNode } from "../tree/tree" ; import { BufferCursor , ContentNode } from "./contentModel" ; /**
 * The maximum length of a buffer string.
 */ export const MAX_BUFFER_LENGTH = 65535 ; /**
 * The sentinel node of red-black trees.
 */ export const SENTINEL_CONTENT = { bufferIndex : 0 , color : Color . Black , end : { column : 0 , line : 0 } , left : SENTINEL_INDEX , leftCharCount : 0 , leftLineFeedCount : 0 , length : 0 , lineFeedCount : 0 , parent : SENTINEL_INDEX , right : SENTINEL_INDEX , start : { column : 0 , line : 0 } , } ; /**
 * A boundary of a `StructureNode`, in regards to its constituent
 * `ContentNode`s.
 */ export interface ContentBoundary { /**
     * The index of the `ContentNode` inside the `RedBlackTree`.
     */ nodeIndex : number ; /**
     * The offset of the boundary of the `StructureNode` in regards to the
     * `ContentNode`.
     */ nodeLocalOffset : number ; } /**
 * The returned object from `findNodeAtOffset`.
 */ export interface NodePositionOffset extends ContentBoundary { /**
     * Piece Index
     */ node : ContentNode ; /**
     * The remainder between the offset and the character count of the left
     * subtree.
     */ remainder : number ; } param tree param offset export function findNodeAtOffset ( tree , offset ) { let x = tree . root ; let nodeLocalOffset = 0 ; while ( x !== SENTINEL_INDEX ) { if ( tree . nodes [ x ] . leftCharCount > offset ) { const oldXIndex = x ; x = tree . nodes [ x ] . left ; if ( x === SENTINEL_INDEX ) { return { node : tree . nodes [ x ] , nodeIndex : oldXIndex , nodeLocalOffset , remainder : 0 , } ; } } else if ( tree . nodes [ x ] . leftCharCount + tree . nodes [ x ] . length > offset ) { nodeLocalOffset += tree . nodes [ x ] . leftCharCount ; return { node : tree . nodes [ x ] , nodeIndex : x , nodeLocalOffset , remainder : offset - tree . nodes [ x ] . leftCharCount , } ; } else { offset -= tree . nodes [ x ] . leftCharCount + tree . nodes [ x ] . length ; const oldnodeLocalOffset = nodeLocalOffset ; nodeLocalOffset += tree . nodes [ x ] . leftCharCount + tree . nodes [ x ] . length ; const oldXIndex = x ; x = tree . nodes [ x ] . right ; if ( x === SENTINEL_INDEX ) { return { node : tree . nodes [ oldXIndex ] , nodeIndex : oldXIndex , nodeLocalOffset : oldnodeLocalOffset , remainder : tree . nodes [ oldXIndex ] . length , } ; } } } console . error ( "Reaching here means that `nodes[x]` is a SENTINEL node, and stored " + "inside the piece table's `nodes` array." ) ; return { node : SENTINEL_CONTENT , nodeIndex : SENTINEL_INDEX , nodeLocalOffset : 0 , remainder : 0 , } ; } param content export function getLineStarts ( content ) { const lineStarts = [ 0 ] ; for ( let i = 1 ; i < content . length ; i ++ ) { const char = content [ i ] ; if ( char === "\n" ) { lineStarts . push ( i + 1 ) ; } } return lineStarts ; } param page param bufferIndex param cursor export function getOffsetInBuffer ( page , bufferIndex , cursor ) { const lineStarts = page . buffers [ bufferIndex ] . lineStarts ; return lineStarts [ cursor . line ] + cursor . column ; } param page param nodeIndex export function getNodeContent ( page , nodeIndex ) { if ( nodeIndex === SENTINEL_INDEX ) { return "" ; } const node = page . content . nodes [ nodeIndex ] as ContentNode ; if ( node . bufferIndex === undefined ) { return "" ; } const buffer = page . buffers [ node . bufferIndex ] ; const startOffset = getOffsetInBuffer ( page , node . bufferIndex , node . start ) ; const endOffset = getOffsetInBuffer ( page , node . bufferIndex , node . end ) ; const currentContent = buffer . content . slice ( startOffset , endOffset ) ; return currentContent ; } param tree param index export function calculateCharCount ( tree , index ) { if ( index === SENTINEL_INDEX ) { return 0 ; } const node = tree . nodes [ index ] ; return ( node . leftCharCount + node . length + calculateCharCount ( tree , node . right ) ) ; } param tree param index export function calculateLineFeedCount ( tree , index ) { if ( index === SENTINEL_INDEX ) { return 0 ; } const node = tree . nodes [ index ] ; return ( node . leftLineFeedCount + node . lineFeedCount + calculateLineFeedCount ( tree , node . right ) ) ; } param tree export function resetSentinelContent ( tree ) { SENTINEL_CONTENT . bufferIndex = 0 ; SENTINEL_CONTENT . start = { column : 0 , line : 0 } ; SENTINEL_CONTENT . end = { column : 0 , line : 0 } ; SENTINEL_CONTENT . leftCharCount = 0 ; SENTINEL_CONTENT . leftLineFeedCount = 0 ; SENTINEL_CONTENT . length = 0 ; SENTINEL_CONTENT . lineFeedCount = 0 ; SENTINEL_CONTENT . color = Color . Black ; SENTINEL_CONTENT . parent = SENTINEL_INDEX ; SENTINEL_CONTENT . left = SENTINEL_INDEX ; SENTINEL_CONTENT . right = SENTINEL_INDEX ; tree . nodes [ 0 ] = SENTINEL_CONTENT ; } param tree param x param charCountDelta param lineFeedCountDelta export function updateContentTreeMetadata ( tree , x , charCountDelta , lineFeedCountDelta ) { while ( x !== tree . root && x !== SENTINEL_INDEX ) { if ( tree . nodes [ tree . nodes [ x ] . parent ] . left === x ) { tree . nodes [ tree . nodes [ x ] . parent ] . leftCharCount = ( tree . nodes [ tree . nodes [ x ] . parent ] as ContentNode ) . leftCharCount + charCountDelta ; tree . nodes [ tree . nodes [ x ] . parent ] . leftLineFeedCount = ( tree . nodes [ tree . nodes [ x ] . parent ] as ContentNode ) . leftLineFeedCount + lineFeedCountDelta ; } x = tree . nodes [ x ] . parent ; } } param page param startOffset param endOffset export function getContentBetweenOffsets ( page , startOffset , endOffset ) { const position = findNodeAtOffset ( page . content , startOffset ) ; const length = endOffset - startOffset ; const startContentIndex = startOffset - position . nodeLocalOffset ; let content = getNodeContent ( page , position . nodeIndex ) . slice ( startContentIndex ) ; let end ; if ( length <= content . length ) { content = content . slice ( 0 , length ) ; end = { nodeIndex : position . nodeIndex , nodeLocalOffset : startContentIndex + length , } ; } else { let offset = startOffset + content . length ; let next = nextNode ( page . content . nodes , position . nodeIndex ) ; let prev = next ; while ( offset < endOffset ) { content += getNodeContent ( page , next . index ) ; offset += next . node . length ; prev = next ; next = nextNode ( page . content . nodes , next . index ) ; } content = content . slice ( 0 , length ) ; end = { nodeIndex : prev . index , nodeLocalOffset : prev . node . length - ( offset - ( startOffset + length ) ) , } ; } return { content , end , start : { nodeIndex : position . nodeIndex , nodeLocalOffset : startContentIndex , } , } ; } export function getContentBetweenNodeAndOffsets ( page , start , end ) { let content = getNodeContent ( page , start . nodeIndex ) ; if ( start . nodeIndex === end . nodeIndex ) { content = content . slice ( start . nodeLocalOffset , end . nodeLocalOffset ) ; return content ; } content = content . slice ( start . nodeLocalOffset ) ; let next = nextNode ( page . content . nodes , start . nodeIndex ) ; while ( next . index !== end . nodeIndex ) { content += getNodeContent ( page , next . index ) ; next = nextNode ( page . content . nodes , next . index ) ; } content += getNodeContent ( page , next . index ) . slice ( 0 , end . nodeLocalOffset ) ; return content ; }