import { LeftRightAtom } from '../core-atoms/leftright' ; import { Atom , Branch } from '../core/atom' ; import type { Range } from '../public/mathfield' ; import { ModelPrivate } from './model-private' ; import { range } from './selection-utils' ; param branch return function onDelete ( model , direction , atom , branch ? ) { const parent = atom . parent ! ; if ( atom instanceof LeftRightAtom ) { const atStart = ( ! branch && direction === 'forward' ) || ( branch === 'body' && direction === 'backward' ) ; const pos = atStart ? model . offsetOf ( atom ) - 1 : model . offsetOf ( atom . lastChild ) ; if ( ! atStart && atom . leftDelim !== '?' && atom . leftDelim !== '.' ) { parent . addChildBefore ( new Atom ( 'mopen' , { value : atom . leftDelim } ) , atom ) ; } else if ( atStart && atom . rightDelim !== '?' && atom . rightDelim !== '.' ) { parent . addChildAfter ( new Atom ( 'mclose' , { value : atom . rightDelim } ) , atom ) ; } parent . addChildrenAfter ( atom . removeBranch ( 'body' ) , atom ) ; parent . removeChild ( atom ) ; model . position = pos ; return true ; } if ( atom . type === 'surd' ) { if ( ( direction === 'forward' && ! branch ) || ( direction === 'backward' && branch === 'body' ) ) { const pos = atom . leftSibling ; if ( atom . hasChildren ) { parent . addChildrenAfter ( atom . removeBranch ( 'body' ) , atom ) ; } parent . removeChild ( atom ) ; model . position = model . offsetOf ( pos ) ; } else if ( direction === 'forward' && branch === 'body' ) { model . position = model . offsetOf ( atom ) ; } else if ( ! branch && direction === 'backward' ) { if ( atom . hasChildren ) { model . position = model . offsetOf ( atom . lastChild ) ; } else { model . position = Math . max ( model . offsetOf ( atom ) - 1 ) ; parent . removeChild ( atom ) ; } } else if ( branch === 'above' ) { if ( atom . hasEmptyBranch ( 'above' ) ) { atom . removeBranch ( 'above' ) ; } if ( direction === 'backward' ) { model . position = model . offsetOf ( atom . leftSibling ) ; } else { model . position = model . offsetOf ( atom . body ! [ 0 ] ) ; } } return true ; } if ( atom . type === 'box' || atom . type === 'enclose' ) { const pos = ( branch && direction === 'backward' ) || ( ! branch && direction === 'forward' ) ? atom . leftSibling : atom . lastChild ; parent . addChildrenAfter ( atom . removeBranch ( 'body' ) , atom ) ; parent . removeChild ( atom ) ; model . position = model . offsetOf ( pos ) ; return true ; } if ( atom . type === 'genfrac' || atom . type === 'overunder' ) { if ( ! branch ) { if ( ! atom . hasChildren ) return false ; model . position = model . offsetOf ( direction === 'forward' ? atom . firstChild : atom . lastChild ) ; return true ; } if ( ( direction === 'forward' && branch === 'above' ) || ( direction === 'backward' && branch === 'below' ) ) { const above = atom . removeBranch ( 'above' ) ; const below = atom . removeBranch ( 'below' ) ; parent . addChildrenAfter ( [ ... above , ... below ] , atom ) ; parent . removeChild ( atom ) ; model . position = model . offsetOf ( above . length > 0 ? above [ above . length - 1 ] : below [ 0 ] ) ; return true ; } if ( direction === 'backward' ) { model . position = model . offsetOf ( atom . leftSibling ) ; return true ; } model . position = model . offsetOf ( atom ) ; return true ; } if ( atom . isExtensibleSymbol || atom . type === 'msubsup' ) { if ( ! branch && direction === 'forward' ) return false ; if ( ! branch ) { if ( atom . subscript || atom . superscript ) { const pos = direction === 'forward' ? atom . superscript ?. [ 0 ] ?? atom . subscript ?. [ 0 ] : atom . subscript ?. [ 0 ] . lastSibling ?? atom . superscript ?. [ 0 ] . lastSibling ; if ( pos ) model . position = model . offsetOf ( pos ) ; return true ; } return false ; } if ( branch && atom . hasEmptyBranch ( branch ) ) { atom . removeBranch ( branch ) ; } if ( ! atom . hasChildren ) { const pos = direction === 'forward' ? model . offsetOf ( atom ) : Math . max ( 0 , model . offsetOf ( atom ) - 1 ) ; atom . parent ! . removeChild ( atom ) ; model . position = pos ; return true ; } if ( branch === 'superscript' ) { if ( direction === 'backward' ) { const pos = model . offsetOf ( atom . firstChild ) - 1 ; console . assert ( pos >= 0 ) ; model . position = pos ; } else if ( atom . subscript ) { model . position = model . offsetOf ( atom . subscript [ 0 ] ) ; } else { model . position = model . offsetOf ( atom ) ; } } else if ( branch === 'subscript' ) { if ( direction === 'backward' && atom . superscript ) { model . position = model . offsetOf ( atom . superscript [ 0 ] . lastSibling ) ; } else if ( direction === 'backward' ) { model . position = model . offsetOf ( atom . firstChild ) - 1 ; } else { model . position = model . offsetOf ( atom ) ; } } return true ; } return false ; } /**
 * Delete the item at the current position
 */ export function deleteBackward ( model ) { if ( ! model . selectionIsCollapsed ) { return deleteRange ( model , range ( model . selection ) ) ; } return model . deferNotifications ( { content : true , selection : true } , ( ) => { let target = model . at ( model . position ) ; if ( target && onDelete ( model , 'backward' , target ) ) return ; if ( target ?. isFirstSibling ) { if ( onDelete ( model , 'backward' , target . parent ! , target . treeBranch ) ) { return ; } target = null ; } if ( ! target ) { model . announce ( 'plonk' ) ; return ; } const offset = model . offsetOf ( target . leftSibling ) ; target . parent ! . removeChild ( target ) ; model . announce ( 'delete' , undefined , [ target ] ) ; model . position = offset ; } ) ; } /**
 * Delete the item forward of the current position, update the position and
 * send notifications
 */ export function deleteForward ( model ) { if ( ! model . selectionIsCollapsed ) { return deleteRange ( model , range ( model . selection ) ) ; } return model . deferNotifications ( { content : true , selection : true } , ( ) => { let target = model . at ( model . position ) . rightSibling ; if ( target && onDelete ( model , 'forward' , target ) ) return ; if ( ! target ) { target = model . at ( model . position ) ; if ( target . isLastSibling && onDelete ( model , 'forward' , target . parent ! , target . treeBranch ) ) { return ; } target = null ; } else if ( model . at ( model . position ) . isLastSibling && onDelete ( model , 'forward' , target . parent ! , target . treeBranch ) ) { return ; } if ( model . position === model . lastOffset || ! target ) { model . announce ( 'plonk' ) ; return ; } target . parent ! . removeChild ( target ) ; let sibling = model . at ( model . position ) ?. rightSibling ; while ( sibling ?. type === 'msubsup' ) { sibling . parent ! . removeChild ( sibling ) ; sibling = model . at ( model . position ) ?. rightSibling ; } model . announce ( 'delete' , undefined , [ target ] ) ; } ) ; } /**
 * Delete the specified range, as a result of a user action: this will
 * account for special cases such as deleting empty denominator, and will
 * provide appropriate feedback to screen readers.
 *
 * Use model.deleteAtoms() for operations that are not a result of
 * user action.
 */ export function deleteRange ( model , range ) { return model . deferNotifications ( { content : true , selection : true } , ( ) => { model . deleteAtoms ( range ) ; model . position = range [ 0 ] ; } ) ; }