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;
}