| 1 | import type { Mention } from './mentions'; |
| 2 | |
| 3 | export interface MarkPart { |
| 4 | text: string; |
| 5 | mark: boolean; |
| 6 | } |
| 7 | |
| 8 | /** The composer text split so picked mention names and emote shortcodes can be highlighted behind the textarea. */ |
| 9 | export function markMentions(text: string, mentions: Mention[], extra: string[] = []): MarkPart[] { |
| 10 | const names = [...new Set([...mentions.map((m) => m.text), ...extra].filter(Boolean))].sort((a, b) => b.length - a.length); |
| 11 | if (!names.length || !text) return [{ text, mark: false }]; |
| 12 | const parts: MarkPart[] = []; |
| 13 | let rest = text; |
| 14 | while (rest) { |
| 15 | let at = -1; |
| 16 | let hit = ''; |
| 17 | for (const name of names) { |
| 18 | const i = rest.indexOf(name); |
| 19 | if (i >= 0 && (at < 0 || i < at)) { |
| 20 | at = i; |
| 21 | hit = name; |
| 22 | } |
| 23 | } |
| 24 | if (at < 0) { |
| 25 | parts.push({ text: rest, mark: false }); |
| 26 | break; |
| 27 | } |
| 28 | if (at > 0) parts.push({ text: rest.slice(0, at), mark: false }); |
| 29 | parts.push({ text: hit, mark: true }); |
| 30 | rest = rest.slice(at + hit.length); |
| 31 | } |
| 32 | return parts; |
| 33 | } |