import { Atom , Branch } from '../core/atom' ; import { parseLatex } from '../core/parser' ; import type { FindOptions , Range , ReplacementFunction , } from '../public/mathfield' ; import { ModelPrivate } from './model-private' ; function match ( pattern , latex ) { if ( typeof pattern === 'string' ) { return pattern === latex ; } return pattern . test ( latex ) ; } function findInBranch ( model , atom , branchName , value , options ? ) { const branch = atom . branch ( branchName ) ; if ( ! branch ) return [ ] ; const result = [ ] ; let { length } = branch ; while ( length > 0 ) { for ( let i = 1 ; i < branch . length - length + 1 ; i ++ ) { const latex = Atom . serialize ( branch . slice ( i , i + length ) , { expandMacro : false , defaultMode : model . mathfield . options . defaultMode , } ) ; if ( match ( value , latex ) ) { result . push ( [ model . offsetOf ( branch [ i ] . leftSibling ) , model . offsetOf ( branch [ i + length - 1 ] ) , ] ) ; i += length ; } } length -- ; } return branch . reduce ( ( acc , x ) => [ ... acc , ... findInAtom ( model , x , value , options ) ] , result ) ; } function findInAtom ( model , atom , value , options ? ) { if ( atom . type === 'first' ) return [ ] ; if ( options ?. mode && options . mode !== atom . mode ) return [ ] ; return atom . branches . reduce ( ( acc , x ) => { return [ ... acc , ... findInBranch ( model , atom , x , value , options ) ] ; } , [ ] ) ; } export function find ( model , value , options ? ) { return findInBranch ( model , model . root , 'body' , value , options ) . sort ( ( a , b ) => { if ( b [ 0 ] === a [ 0 ] ) { return b [ 1 ] - a [ 1 ] ; } return b [ 0 ] - a [ 0 ] ; } ) ; } function replaceInBranch ( model , atom , branchName , pattern , replacement , options ? ) { const branch = atom . branch ( branchName ) ; if ( ! branch ) return ; branch . forEach ( ( x ) => replaceInAtom ( model , x , pattern , replacement , options ) ) ; let i = 1 ; while ( i < branch . length ) { let length = branch . length - i ; while ( length > 0 ) { let matched = false ; const latex = Atom . serialize ( branch . slice ( i , i + length ) , { expandMacro : false , defaultMode : model . mathfield . options . defaultMode , } ) ; const replacementArgs = { latex } ; if ( typeof pattern === 'string' && latex === pattern ) { matched = true ; } else if ( pattern instanceof RegExp ) { const match = latex . match ( pattern ) ; if ( match !== null ) { matched = true ; if ( match . length > 0 ) { replacementArgs . p = [ ... match ] ; } replacementArgs . groups = match . groups ; } } if ( matched ) { for ( let j = i + length - 1 ; j >= i ; j -- ) { atom . removeChild ( branch [ j ] ) ; } let replacementString ; if ( typeof replacement === 'string' ) { replacementString = replacement ; if ( replacementArgs . p ) { replacementArgs . p . forEach ( ( x , index ) => { if ( typeof x === 'string' ) { replacementString = replacementString . replace ( '$' + Number ( index ) . toString ( ) , x ) ; } } ) ; } if ( replacementArgs . groups ) { Object . keys ( replacementArgs . groups ) . forEach ( ( x ) => { if ( typeof x === 'string' ) { replacementString = replacementString . replace ( '$' + x , replacementArgs . groups [ x ] ) ; } } ) ; } replacementString = replacementString . replace ( '$$' , '$' ) ; } else { replacementString = replacement ( replacementArgs ) ; } const lastChild = atom . addChildrenAfter ( parseLatex ( replacementString , { parseMode : atom . mode } ) , branch [ i - 1 ] ) ; i = branch . indexOf ( lastChild ) + 1 ; length = branch . length - i ; } else { length -- ; } } i ++ ; } } function replaceInAtom ( model , atom , pattern , replacement , options ? ) { if ( atom . type === 'first' ) return ; if ( options ?. mode && options . mode !== atom . mode ) return ; atom . branches . forEach ( ( x ) => replaceInBranch ( model , atom , x , pattern , replacement , options ) ) ; } export function replace ( model , pattern , replacement , options ? ) { replaceInBranch ( model , model . root , 'body' , pattern , replacement , options ) ; model . position = model . normalizeOffset ( model . position ) ; }