import type { Universe } from '@ephox/boss'; import type { Optional } from '@ephox/katamari'; export interface WordDecisionItem { readonly item: E; readonly start: number; readonly finish: number; readonly text: string; } export interface WordDecision { readonly items: WordDecisionItem[]; readonly abort: boolean; } const make = (item: E, start: number, finish: number, text: string): WordDecisionItem => ({ item, start, finish, text }); const decision = (items: WordDecisionItem[], abort: boolean): WordDecision => ({ items, abort }); const detail = (universe: Universe, item: E): WordDecisionItem => { const text = universe.property().getText(item); return make(item, 0, text.length, text); }; const fromItem = (universe: Universe, item: E): WordDecisionItem => { return universe.property().isText(item) ? detail(universe, item) : make(item, 0, 0, ''); }; const onEdge = (_universe: Universe, _item: E, _slicer: (text: string) => Optional<[number, number]>): WordDecision => { return decision([], true); }; const onOther = (_universe: Universe, _item: E, _slicer: (text: string) => Optional<[number, number]>): WordDecision => { return decision([], false); }; // Returns: a 'decision' Struct with the items slot containing an empty array if None // or a zero-width [start, end] range was returned by slicer, or 1-element array of the // [start, end] substring otherwise. const onText = (universe: Universe, item: E, slicer: (text: string) => Optional<[number, number]>): WordDecision => { const text = universe.property().getText(item); return slicer(text).fold(() => { return decision([ make(item, 0, text.length, text) ], false); }, (splits) => { const items = splits[0] === splits[1] ? [] : [ make(item, splits[0], splits[1], text.substring(splits[0], splits[1])) ]; return decision(items, true); }); }; // Return decision struct with one or zero 'make' Struct items. If present the make struct item is the entire item node text, // or a substring of it with the [left, right] bounds as determined by the result of slicer(item). const decide = (universe: Universe, item: E, slicer: (text: string) => Optional<[number, number]>, isCustomBoundary: (universe: Universe, item: E) => boolean): WordDecision => { const f = (() => { if (universe.property().isBoundary(item)) { return onEdge; } else if (universe.property().isEmptyTag(item)) { return onEdge; } else if (isCustomBoundary(universe, item)) { return onEdge; } else if (universe.property().isText(item)) { return onText; } else { return onOther; } })(); return f(universe, item, slicer); }; export const WordDecision = { detail, fromItem, decide };