Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 36x 36x 36x 30x 30x 36x 13x 13x 30x 30x 30x 30x 30x 30x 30x 30x 30x 32x 32x 32x 12x 12x 32x 32x 32x 1x 1x 31x 31x 31x 1x 1x 1x 35x 35x 35x 35x 35x 45x 45x 45x 45x 35x 35x | /**
* Generic entity marker parser.
*
* Walks text with a configurable regex and delegates to a config-provided
* parseMatch function to build domain-specific entity refs. The Euclid
* consumer uses this for geometric markers like {seg:AB}, {tri:ABC}, etc.
*/
import type { EntityMarkerConfig } from './types'
export type MarkedSegment<TEntityRef> =
| { kind: 'text'; text: string }
| { kind: 'entity'; text: string; entity: TEntityRef }
export function parseEntityMarkers<TEntityRef>(
text: string,
config: EntityMarkerConfig<TEntityRef>
): MarkedSegment<TEntityRef>[] {
const result: MarkedSegment<TEntityRef>[] = []
let lastIndex = 0
// Reset regex state (required for `g` flag regexes)
config.pattern.lastIndex = 0
let match: RegExpExecArray | null
while ((match = config.pattern.exec(text)) !== null) {
const groups = match.slice(1) // capture groups only
const parsed = config.parseMatch(groups)
if (!parsed) continue
// Add preceding plain text
if (match.index > lastIndex) {
result.push({ kind: 'text', text: text.slice(lastIndex, match.index) })
}
result.push({
kind: 'entity',
text: parsed.displayText,
entity: parsed.entity,
})
lastIndex = match.index + match[0].length
}
// Add trailing text
if (lastIndex < text.length) {
result.push({ kind: 'text', text: text.slice(lastIndex) })
}
// If no markers found, return the whole thing as text
if (result.length === 0) {
return [{ kind: 'text', text }]
}
return result
}
/** Replace all entity markers with their display text, returning a plain string. */
export function stripEntityMarkers<TEntityRef>(
text: string,
config: EntityMarkerConfig<TEntityRef>
): string {
config.pattern.lastIndex = 0
return text.replace(config.pattern, (...args) => {
// args: full match, then capture groups, then index, then input
const groups = args.slice(1, -2) as string[]
const parsed = config.parseMatch(groups)
return parsed ? parsed.displayText : args[0]
})
}
|