import { deleteNode , deleteBetweenNodes } from "../tree/delete" ; import { Color , PageContent } from "../pageModel" ; import { nextNode , SENTINEL_INDEX , recomputeTreeMetadata , resetSentinel , } from "../tree/tree" ; import { ContentNode } from "./contentModel" ; import { insertNode , fixInsert } from "../tree/insert" ; import { findNodeAtOffset , NodePositionOffset } from "./tree" ; /**
 * The logical offset range for the content to be deleted.
 */ export interface ContentDelete { startOffset : number ; endOffset : number ; } param page param nodePosition param startLocalOffset param endLocalOffset function getLineFeedCountsForOffsets ( page , nodePosition , startLocalOffset , endLocalOffset ) { const buffer = page . buffers [ nodePosition . node . bufferIndex ] ; const nodeLocalOffset = page . buffers [ nodePosition . node . bufferIndex ] . lineStarts [ nodePosition . node . start . line ] + nodePosition . node . start . column ; let lineFeedCountBeforeNodeStart = 0 ; let lineFeedCountAfterNodeStartBeforeStart = 0 ; let lineFeedCountBetweenOffset = 0 ; let lineFeedCountAfterEnd = 0 ; for ( let i = 1 ; i < buffer . lineStarts . length ; i ++ ) { const el = buffer . lineStarts [ i ] ; if ( el < nodeLocalOffset ) { lineFeedCountBeforeNodeStart ++ ; } else if ( nodeLocalOffset <= el && el < startLocalOffset ) { lineFeedCountAfterNodeStartBeforeStart ++ ; } else if ( startLocalOffset <= el && el < endLocalOffset ) { lineFeedCountBetweenOffset ++ ; } else if ( endLocalOffset <= el ) { lineFeedCountAfterEnd ++ ; } } return { lineFeedCountAfterEnd , lineFeedCountAfterNodeStartBeforeStart , lineFeedCountBeforeNodeStart , lineFeedCountBetweenOffset , } ; } param page param deleteRange param nodePosition function getNodeAfterContent ( page , deleteRange , nodePosition ) { const localStartOffset = page . buffers [ nodePosition . node . bufferIndex ] . lineStarts [ nodePosition . node . start . line ] + nodePosition . node . start . column ; const deletedLength = deleteRange . endOffset - deleteRange . startOffset ; const firstSection = nodePosition . nodeLocalOffset - deleteRange . startOffset ; const secondSection = deletedLength - firstSection ; const localEndOffset = localStartOffset + secondSection + 1 ; const length = nodePosition . nodeLocalOffset + nodePosition . node . length - deleteRange . endOffset ; const { lineFeedCountAfterNodeStartBeforeStart , lineFeedCountBetweenOffset , } = getLineFeedCountsForOffsets ( page , nodePosition , localEndOffset , localEndOffset + length ) ; const nodeAfterContentStartLine = nodePosition . node . start . line + lineFeedCountAfterNodeStartBeforeStart ; const lineStartOffset = page . buffers [ nodePosition . node . bufferIndex ] . lineStarts [ nodeAfterContentStartLine ] ; const nodeAfterContent = { bufferIndex : nodePosition . node . bufferIndex , color : Color . Red , end : nodePosition . node . end , left : 0 , leftCharCount : 0 , leftLineFeedCount : 0 , length , lineFeedCount : lineFeedCountBetweenOffset , parent : 0 , right : 0 , start : { column : localEndOffset - lineStartOffset - 1 , line : nodeAfterContentStartLine , } , } ; return nodeAfterContent ; } param page param deleteRange param nodePosition function getNodeBeforeContent ( page , deleteRange , { node , nodeIndex , nodeLocalOffset , remainder } ) { const localStartOffset = page . buffers [ node . bufferIndex ] . lineStarts [ node . start . line ] + node . start . column + remainder ; const deletedLength = deleteRange . endOffset - deleteRange . startOffset ; const localEndOffset = localStartOffset + deletedLength + 1 ; const { lineFeedCountBeforeNodeStart , lineFeedCountAfterNodeStartBeforeStart , } = getLineFeedCountsForOffsets ( page , { node , nodeIndex , nodeLocalOffset , remainder } , localStartOffset , localEndOffset ) ; const nodeBeforeContent = { ... node , end : { column : localStartOffset - page . buffers [ node . bufferIndex ] . lineStarts [ lineFeedCountBeforeNodeStart + lineFeedCountAfterNodeStartBeforeStart ] , line : lineFeedCountBeforeNodeStart + lineFeedCountAfterNodeStartBeforeStart , } , length : remainder , lineFeedCount : lineFeedCountAfterNodeStartBeforeStart , } ; return nodeBeforeContent ; } param page param index param newNode function updateNode ( page , index , newNode ) { newNode . leftCharCount = page . content . nodes [ index ] . leftCharCount ; newNode . leftLineFeedCount = page . content . nodes [ index ] . leftLineFeedCount ; newNode . parent = page . content . nodes [ index ] . parent ; newNode . left = page . content . nodes [ index ] . left ; newNode . right = page . content . nodes [ index ] . right ; newNode . color = page . content . nodes [ index ] . color ; page . content . nodes [ index ] = newNode ; recomputeTreeMetadata ( page . content , index ) ; } param page param deleteRange export function deleteContent ( page , deleteRange ) { const oldNodeStartPosition = findNodeAtOffset ( page . content , deleteRange . startOffset ) ; let oldNodeEndPosition ; const nodeBeforeContent = getNodeBeforeContent ( page , deleteRange , oldNodeStartPosition ) ; const deleteLength = deleteRange . endOffset - deleteRange . startOffset ; let nodeAfterContent ; if ( oldNodeStartPosition . remainder + deleteLength <= oldNodeStartPosition . node . length ) { nodeAfterContent = getNodeAfterContent ( page , deleteRange , oldNodeStartPosition ) ; oldNodeEndPosition = oldNodeStartPosition ; } else { oldNodeEndPosition = findNodeAtOffset ( page . content , deleteRange . endOffset ) ; nodeAfterContent = getNodeAfterContent ( page , deleteRange , oldNodeEndPosition ) ; } let firstNodeToDelete = oldNodeStartPosition . nodeIndex ; let nodeAfterLastNodeToDelete = oldNodeEndPosition . nodeIndex ; if ( oldNodeStartPosition === oldNodeEndPosition && nodeBeforeContent . length > 0 && nodeAfterContent . length > 0 ) { page . content . nodes [ oldNodeStartPosition . nodeIndex ] = nodeBeforeContent ; insertNode ( page . content , nodeAfterContent , deleteRange . startOffset ) ; fixInsert ( page . content , page . content . nodes . length - 1 ) ; } else if ( nodeBeforeContent . length > 0 && nodeAfterContent . length > 0 ) { updateNode ( page , oldNodeStartPosition . nodeIndex , nodeBeforeContent ) ; updateNode ( page , oldNodeEndPosition . nodeIndex , nodeAfterContent ) ; firstNodeToDelete = nextNode ( page . content . nodes , firstNodeToDelete ) . index ; } else if ( nodeBeforeContent . length > 0 ) { page . content . nodes [ oldNodeStartPosition . nodeIndex ] = nodeBeforeContent ; if ( oldNodeStartPosition !== oldNodeEndPosition ) { deleteNode ( page . content , oldNodeEndPosition . nodeIndex ) ; nodeAfterLastNodeToDelete = SENTINEL_INDEX ; firstNodeToDelete = nextNode ( page . content . nodes , firstNodeToDelete ) . index ; } } else if ( nodeAfterContent . length > 0 ) { updateNode ( page , oldNodeEndPosition . nodeIndex , nodeAfterContent ) ; } else if ( oldNodeStartPosition === oldNodeEndPosition ) { deleteNode ( page . content , oldNodeStartPosition . nodeIndex ) ; } else { nodeAfterLastNodeToDelete = nextNode ( page . content . nodes , nodeAfterLastNodeToDelete ) . index ; } page . previouslyInsertedContentNodeIndex = null ; page . previouslyInsertedContentNodeOffset = null ; if ( oldNodeStartPosition . nodeIndex !== oldNodeEndPosition . nodeIndex ) { deleteBetweenNodes ( page . content , firstNodeToDelete , nodeAfterLastNodeToDelete ) ; } resetSentinel ( page . content ) ; }