import { Side , Snapshot } from '../Snapshot' ; import { Change , ChangeType , placeFromStablePlace , StablePlace , StableRange , validateStablePlace , validateStableRange , } from '../default-edits' ; import { assert , assertNotUndefined , fail } from '../Common' ; import { EditValidationResult } from '../Checkout' ; import { NodeId } from '../Identifiers' ; import { ReconciliationPath } from '../ReconciliationPath' ; import { AnchoredChange , NodeAnchor , PlaceAnchor , PlaceAnchorSemanticsChoice , RangeAnchor , RelativePlaceAnchor , } from './PersistedTypes' ; /**
 * A change and the snapshots that precede and succeed it.
 */ export interface EvaluatedChange < TChange > { readonly change : TChange ; /**
     * The snapshot before the change was applied.
     */ readonly before : Snapshot ; /**
     * The snapshot after the change was applied.
     */ readonly after : Snapshot ; } /**
 * Object that includes a function for resolving node anchors.
 */ export interface HasNodeResolver { nodeResolver : ( node : NodeAnchor , before : Snapshot , path : ReconciliationPath < AnchoredChange > ) => NodeId | undefined ; } /**
 * Object that includes a function for resolving place anchors.
 */ export interface HasPlaceResolver { placeResolver : ( range : PlaceAnchor , before : Snapshot , path : ReconciliationPath < AnchoredChange > ) => StablePlace | undefined ; } /**
 * Object that includes a function for resolving range anchors.
 */ export interface HasRangeResolver { rangeResolver : ( range : RangeAnchor , before : Snapshot , path : ReconciliationPath < AnchoredChange > ) => StableRange | undefined ; } /**
 * Object that includes a function for validating places.
 */ export interface HasPlaceValidator { placeValidator : ( snapshot : Snapshot , place : StablePlace ) => EditValidationResult ; } param change param before param path returns internal export function resolveChangeAnchors ( change , before , path ) ; internal export function resolveChangeAnchors ( change , before , path , dependencies ? ) ; export function resolveChangeAnchors ( change , before , path , { nodeResolver , placeResolver , rangeResolver } = { nodeResolver : resolveNodeAnchor , placeResolver : resolvePlaceAnchor , rangeResolver : resolveRangeAnchor } ) { switch ( change . type ) { case ChangeType . Build : return change ; case ChangeType . Insert : { const destination = placeResolver ( change . destination , before , path ) ; return destination !== undefined ? { ... change , destination } : undefined ; } case ChangeType . Detach : { const source = rangeResolver ( change . source , before , path ) ; return source !== undefined ? { ... change , source } : undefined ; } case ChangeType . Constraint : { const toConstrain = rangeResolver ( change . toConstrain , before , path ) ; return toConstrain !== undefined ? { ... change , toConstrain } : undefined ; } case ChangeType . SetValue : { const nodeToModify = nodeResolver ( change . nodeToModify , before , path ) ; return nodeToModify !== undefined ? { ... change , nodeToModify } : undefined ; } default : return fail ( 'Attempted to reconciliate unsupported change' ) ; } } param node param before param path returns internal export function resolveNodeAnchor ( node , before , path ) { return before . hasNode ( node ) ? node : undefined ; } param range param before param path returns internal export function resolveRangeAnchor ( range , before , path ) ; internal export function resolveRangeAnchor ( range , before , path , dependencies ? ) ; export function resolveRangeAnchor ( range , before , path , { placeResolver , rangeValidator , } = { placeResolver : resolvePlaceAnchor , rangeValidator : validateStableRange } ) { const start = placeResolver ( range . start , before , path ) ; if ( start !== undefined ) { const end = placeResolver ( range . end , before , path ) ; if ( end !== undefined ) { const resolvedRange = { start , end } ; return rangeValidator ( before , resolvedRange ) === EditValidationResult . Valid ? resolvedRange : undefined ; } } return undefined ; } param place param before param path returns internal export function resolvePlaceAnchor ( place , before , path ) ; internal export function resolvePlaceAnchor ( place , before , path , dependencies ? ) ; export function resolvePlaceAnchor ( place , before , path , { placeValidator , placeUpdatorForPath , } = { placeValidator : validateStablePlace , placeUpdatorForPath : updateRelativePlaceAnchorForPath } ) { let newPlace = place ; while ( newPlace !== undefined && placeValidator ( before , newPlace ) !== EditValidationResult . Valid ) { switch ( newPlace . semantics ) { case PlaceAnchorSemanticsChoice . RelativeToNode : { newPlace = placeUpdatorForPath ( newPlace as RelativePlaceAnchor , path ) ; break ; } case PlaceAnchorSemanticsChoice . BoundToNode : case undefined : newPlace = undefined ; break ; default : fail ( 'Unsupported choice of PlaceAnchorSemanticsChoice' ) ; } } return newPlace ; } param place param path returns internal export function updateRelativePlaceAnchorForPath ( place , path ) ; internal export function updateRelativePlaceAnchorForPath ( place , path , dependencies ? ) ; export function updateRelativePlaceAnchorForPath ( place , path , { lastOffendingChangeFinder , placeUpdatorForChange , } = { lastOffendingChangeFinder : findLastOffendingChange , placeUpdatorForChange : updateRelativePlaceAnchorForChange } ) { if ( place . referenceSibling === undefined ) { return undefined ; } const lastOffendingChange = lastOffendingChangeFinder ( place , path ) ; return lastOffendingChange === undefined ? undefined : placeUpdatorForChange ( place , lastOffendingChange ) ; } param place param path returns internal export function findLastOffendingChange ( place , path ) ; internal export function findLastOffendingChange ( place , path , dependencies ? ) ; export function findLastOffendingChange ( place , path , { placeValidator } = { placeValidator : validateStablePlace } ) { let followingChange ; for ( let editIndex = path . length - 1 ; editIndex >= 0 ; -- editIndex ) { const edit = path [ editIndex ] ; for ( let changeIndex = edit . length - 1 ; changeIndex >= 0 ; -- changeIndex ) { const change = edit [ changeIndex ] ; const placeStatusAfterChange = placeValidator ( change . after , place ) ; if ( placeStatusAfterChange === EditValidationResult . Valid ) { return { before : change . after , ... assertNotUndefined ( followingChange , 'The last change should not make the place valid' ) } ; } followingChange = { change : change . resolvedChange , after : change . after } ; } } return path . length > 0 && placeValidator ( path [ 0 ] . before , place ) === EditValidationResult . Valid ? { before : path [ 0 ] . before , ... assertNotUndefined ( followingChange , 'The last change should not make the place valid' ) } : undefined ; } param place param change returns internal export function updateRelativePlaceAnchorForChange ( place , change ) ; export function updateRelativePlaceAnchorForChange ( place , { change , before } ) { if ( place . referenceSibling === undefined ) { return undefined ; } assert ( change . type === ChangeType . Detach , 'A PlaceAnchor can only be made invalid by a detach change' ) ; const targetPlace = placeFromStablePlace ( before , place ) ; const startPlace = placeFromStablePlace ( before , change . source . start ) ; const endPlace = placeFromStablePlace ( before , change . source . end ) ; if ( targetPlace . trait . parent !== startPlace . trait . parent ) { return undefined ; } let newIndex ; if ( targetPlace . side === Side . After ) { newIndex = before . findIndexWithinTrait ( startPlace ) - 1 ; } if ( targetPlace . side === Side . Before ) { newIndex = before . findIndexWithinTrait ( endPlace ) ; } const referenceTrait = targetPlace . trait ; const parentNode = before . getSnapshotNode ( referenceTrait . parent ) ; const traits = new Map ( parentNode . traits ) ; const trait = assertNotUndefined ( traits . get ( referenceTrait . label ) , 'The trait must have been populated before the deletion' ) ; const referenceSibling = trait [ newIndex ] ; if ( referenceSibling === undefined ) { return { referenceTrait , side : place . side , semantics : place . semantics } ; } return { referenceSibling , side : place . side , semantics : place . semantics } ; }