File size: 1,414 Bytes
f59fbe2 | 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 | const SURROGATE_PAIR_AT_START = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/;
const SURROGATE_PAIR_EXACT = /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/;
export interface MentionMatchSpan {
text: string;
hit: boolean;
}
/**
* Split `text` into hit/plain runs from match positions. `positions` index
* into the full path; `start` shifts them into this text's frame.
*/
export function mentionMatchSpans(
text: string,
positions: readonly number[] | undefined,
start: number,
): MentionMatchSpan[] {
if (positions === undefined || positions.length === 0 || text.length === 0) {
return [{ text, hit: false }];
}
const hits = new Set<number>();
for (const pos of positions) {
const i = pos - start;
if (i >= 0 && i < text.length) hits.add(i);
}
if (hits.size === 0) return [{ text, hit: false }];
for (const i of Array.from(hits)) {
if (SURROGATE_PAIR_AT_START.test(text.slice(i, i + 2))) {
hits.add(i + 1);
} else if (SURROGATE_PAIR_EXACT.test(text.slice(i - 1, i + 1))) {
hits.add(i - 1);
}
}
const spans: MentionMatchSpan[] = [];
let runStart = 0;
let runHit = hits.has(0);
for (let i = 1; i < text.length; i++) {
const hit = hits.has(i);
if (hit !== runHit) {
spans.push({ text: text.slice(runStart, i), hit: runHit });
runStart = i;
runHit = hit;
}
}
spans.push({ text: text.slice(runStart), hit: runHit });
return spans;
}
|