import { eventToChar , mightProducePrintableCharacter , } from '../editor/keyboard' ; import { contentDidChange } from '../editor-model/listeners' ; import type { MathfieldPrivate } from './mathfield-private' ; import { ModelPrivate } from '../editor-model/model-private' ; import { Atom } from '../core/atom-class' ; import { LeftRightAtom } from '../core-atoms/leftright' ; param count param { function } until private function convertLastAtomsToText ( model , count ? , until ? ) { if ( typeof count === 'function' ) { until = count ; count = Infinity ; } if ( count === undefined ) { count = Infinity ; } let i = model . position ; let done = false ; while ( ! done ) { const atom = model . at ( i ) ; done = count === 0 || atom === undefined || atom . mode !== 'math' || ! ( /mord|textord|mpunct/ . test ( atom . type ) || ( atom . type === 'mop' && /[a-zA-Z]+/ . test ( atom . value ) ) ) || ! atom . hasEmptyBranch ( 'superscript' ) || ! atom . hasEmptyBranch ( 'subscript' ) || ( typeof until === 'function' && ! until ( atom ) ) ; if ( ! done ) { atom . mode = 'text' ; atom . command = atom . value ; atom . verbatimLatex = undefined ; } i -= 1 ; count -= 1 ; } contentDidChange ( model ) ; } param { number } count param { function } until private function convertLastAtomsToMath ( model , count ? , until ? ) { if ( typeof count === 'function' ) { until = count ; count = Infinity ; } if ( count === undefined ) { count = Infinity ; } let i = model . position ; let done = false ; while ( ! done ) { const atom = model . at ( i ) ; done = count === 0 || ! atom || atom . isFirstSibling || atom . mode !== 'text' || atom . value === ' ' || ( until && ! until ( atom ) ) ; if ( ! done ) { atom . mode = 'math' ; } i -= 1 ; count -= 1 ; } removeIsolatedSpace ( model ) ; contentDidChange ( model ) ; } /**
 * Going backwards from the anchor, if a text zone consisting of a single
 * space character is found (i.e. it is surrounded by math zone),
 * remove it.
 */ export function removeIsolatedSpace ( model ) { let i = model . position - 1 ; while ( i >= 0 && model . at ( i ) ?. mode === 'math' ) { i -= 1 ; } if ( i < 0 ) return ; if ( model . at ( i ) . mode === 'text' && model . at ( i ) . value === ' ' && model . at ( i - 1 ) . mode === 'math' ) { model . at ( i - 1 ) . parent ! . removeChild ( model . at ( i - 1 ) ) ; contentDidChange ( model ) ; const save = model . suppressChangeNotifications ; model . suppressChangeNotifications = true ; model . position -= 1 ; model . suppressChangeNotifications = save ; } } /**
 * Return the characters before the insertion point that could potentially be
 * turned into text mode.
 * This excludes things like 'mop' (e.g. \sin)
 */ function getTextBeforePosition ( model ) { let result = '' ; let i = model . position ; let done = false ; while ( ! done ) { const atom = model . at ( i ) ; done = ! ( atom && ( atom . mode === 'text' || ( atom . mode === 'math' && /mord|textord|mpunct/ . test ( atom . type ) ) ) ) ; if ( ! done ) { result = atom . value + result ; } i -= 1 ; } return result ; } param keystroke param evt return export function smartMode ( mathfield , keystroke , evt ? ) { if ( mathfield . smartModeSuppressed ) { return false ; } const { model } = mathfield ; if ( ! model . at ( model . position ) . isLastSibling ) { return false ; } if ( ! evt || ! mightProducePrintableCharacter ( evt ) ) { return false ; } const c = eventToChar ( evt ) ; if ( ! model . selectionIsCollapsed ) { if ( mathfield . mode === 'text' ) { if ( /[/_^]/ . test ( c ) ) { return true ; } } return false ; } const context = getTextBeforePosition ( model ) + c ; if ( mathfield . mode === 'text' ) { if ( keystroke === 'Esc' || /[/\\]/ . test ( c ) ) { return true ; } if ( /[\^_]/ . test ( c ) ) { if ( /(^|\s)[a-zA-Z][^_]$/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; } return true ; } const lFence = { ')' : '(' , '}' : '{' , ']' : '[' } [ c ] ; const { parent } = model . at ( model . position ) ; if ( lFence && parent instanceof LeftRightAtom && parent . leftDelim === lFence ) { return true ; } if ( /(^|[^a-zA-Z])(a|I) $/ . test ( context ) ) { return false ; } if ( /[$€£₤₺¥¤฿¢₡₧₨₹₩₱]/u . test ( c ) ) { return true ; } if ( /(^|[^a-zA-Z'’])[a-zA-Z] $/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; return false ; } if ( /\D\.[^\d\s]$/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; const atom = model . at ( model . position ) ; atom . value = '⋅' ; atom . style . variant = 'normal' ; atom . command = '\\cdot' ; atom . verbatimLatex = undefined ; contentDidChange ( model ) ; return true ; } if ( /(^|\s)[a-zA-Z][^a-zA-Z]$/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; return true ; } if ( /\.\d$/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; return true ; } if ( /\([\d+\-.]$/ . test ( context ) ) { convertLastAtomsToMath ( model , 1 ) ; return true ; } if ( /\([a-z][,;]$/ . test ( context ) ) { convertLastAtomsToMath ( model , 2 ) ; return true ; } if ( /[\d+\-=><*|]$/ . test ( c ) ) { removeIsolatedSpace ( model ) ; return true ; } } else { if ( keystroke === '[Space]' ) { convertLastAtomsToText ( model , undefined , ( a ) => /[a-z][:,;.]$/ . test ( a . value ) ) ; return true ; } if ( /[a-zA-Z]{3,}$/ . test ( context ) && ! /(dxd|abc|xyz|uvw)$/ . test ( context ) ) { convertLastAtomsToText ( model , undefined , ( a ) => /[a-zA-Z]/ . test ( a . value ) ) ; return true ; } if ( /(^|\W)(if)$/i . test ( context ) ) { convertLastAtomsToText ( model , 1 ) ; return true ; } if ( /(\u0393|\u0394|\u0398|\u039B|\u039E|\u03A0|\u03A3|\u03A5|\u03A6|\u03A8|\u03A9|[\u03B1-\u03C9]|\u03D1|\u03D5|\u03D6|\u03F1|\u03F5){3,}$/u . test ( context ) && ! /(αβγ)$/ . test ( context ) ) { convertLastAtomsToText ( model , undefined , ( a ) => /(:|,|;|.|\u0393|\u0394|\u0398|\u039B|\u039E|\u03A0|\u03A3|\u03A5|\u03A6|\u03A8|\u03A9|[\u03B1-\u03C9]|\u03D1|\u03D5|\u03D6|\u03F1|\u03F5)/u . test ( a . value ) ) ; return true ; } if ( c === '?' ) { return true ; } if ( c === '.' && ! /[\d-+]\.$/ . test ( context ) ) { return true ; } } return false ; }