+ {lines.map((tokens, ln) => {
+ if (isHidden(ln)) return null;
+ const end = startToEnd.get(ln);
+ const folded = end != null && collapsed.has(ln);
+ let pos = 0;
+ const lineMatches = matches.map((m, idx) => ({ ...m, idx })).filter((m) => m.line === ln);
+ const content: React.ReactNode[] = [];
+ tokens.forEach((tok, ti) => {
+ const textSeg = tok.text;
+ const tokenStart = pos;
+ const tokenEnd = pos + textSeg.length;
- if (!query || lineMatches.length === 0) {
- const cls = tok.type === 'plain' ? undefined : `tok-${tok.type}`;
- content.push(
{textSeg});
- pos = tokenEnd;
- return;
- }
+ if (!query || lineMatches.length === 0) {
+ const cls = tok.type === "plain" ? undefined : `tok-${tok.type}`;
+ content.push(
+
+ {textSeg}
+ ,
+ );
+ pos = tokenEnd;
+ return;
+ }
- // Collect matches that intersect this token
- const matchesInToken = lineMatches
- .filter(m => m.start < tokenEnd && m.end > tokenStart)
- .sort((a, b) => a.start - b.start);
+ // Collect matches that intersect this token
+ const matchesInToken = lineMatches
+ .filter((m) => m.start < tokenEnd && m.end > tokenStart)
+ .sort((a, b) => a.start - b.start);
- if (matchesInToken.length === 0) {
- const cls = tok.type === 'plain' ? undefined : `tok-${tok.type}`;
- content.push(
{textSeg});
- pos = tokenEnd;
- return;
- }
+ if (matchesInToken.length === 0) {
+ const cls = tok.type === "plain" ? undefined : `tok-${tok.type}`;
+ content.push(
+
+ {textSeg}
+ ,
+ );
+ pos = tokenEnd;
+ return;
+ }
- let cursor = 0;
- const tokenCls = tok.type === 'plain' ? '' : `tok-${tok.type}`;
+ let cursor = 0;
+ const tokenCls = tok.type === "plain" ? "" : `tok-${tok.type}`;
- matchesInToken.forEach((m, mi) => {
- const localStart = Math.max(0, m.start - tokenStart);
- const localEnd = Math.min(textSeg.length, m.end - tokenStart);
+ matchesInToken.forEach((m, mi) => {
+ const localStart = Math.max(0, m.start - tokenStart);
+ const localEnd = Math.min(textSeg.length, m.end - tokenStart);
- // before match
- if (localStart > cursor) {
- const beforeText = textSeg.slice(cursor, localStart);
- const cls = tokenCls || undefined;
- content.push(
{beforeText});
- }
- // matched piece
- const hitText = textSeg.slice(localStart, localEnd);
- const hitCls = ['search-hit', (m.idx === active ? 'search-hit-active' : ''), tokenCls]
- .filter(Boolean).join(' ') || undefined;
- content.push(
{hitText});
- cursor = localEnd;
- });
+ // before match
+ if (localStart > cursor) {
+ const beforeText = textSeg.slice(cursor, localStart);
+ const cls = tokenCls || undefined;
+ content.push(
+
+ {beforeText}
+ ,
+ );
+ }
+ // matched piece
+ const hitText = textSeg.slice(localStart, localEnd);
+ const hitCls =
+ ["search-hit", m.idx === active ? "search-hit-active" : "", tokenCls].filter(Boolean).join(" ") ||
+ undefined;
+ content.push(
+
+ {hitText}
+ ,
+ );
+ cursor = localEnd;
+ });
- // tail after last match
- if (cursor < textSeg.length) {
- const tailText = textSeg.slice(cursor);
- const cls = tokenCls || undefined;
- content.push(
{tailText});
- }
+ // tail after last match
+ if (cursor < textSeg.length) {
+ const tailText = textSeg.slice(cursor);
+ const cls = tokenCls || undefined;
+ content.push(
+
+ {tailText}
+ ,
+ );
+ }
- pos = tokenEnd;
- });
- return (
-
-
- {end != null ? (
-
- ) : }
- {ln + 1}
-
-
- {content}
- {folded && (
- {"{...}"}
- )}
-
-
- );
- })}
-