import { bytePropertiesNote, detectLineEnding, splitBom, splitLines } from "./text.js"; export type HashReadTextOptions = { startLine?: number; totalLineCount?: number; }; const CONTINUATION_NOTICE = /\r?\n\n(\[(?:Showing lines \d+-\d+ of \d+(?: \([^\]]+\))?\. Use offset=\d+ to continue\.|\d+ more lines in file\. Use offset=\d+ to continue\.)\])$/; function numberReadLines(text: string, options: HashReadTextOptions = {}): string { const noticeMatch = text.match(CONTINUATION_NOTICE); const rawBody = noticeMatch ? text.slice(0, noticeMatch.index) : text; const nextOffset = noticeMatch ? Number(noticeMatch[1]!.match(/Use offset=(\d+) to continue\./)?.[1]) : undefined; const hasRealContinuation = nextOffset === undefined || options.totalLineCount === undefined || nextOffset <= options.totalLineCount; const suffix = noticeMatch && hasRealContinuation ? `\n\n${noticeMatch[1]!}` : ""; const startLine = Number.isInteger(options.startLine) && options.startLine! > 0 ? options.startLine! : 1; const body = startLine === 1 ? splitBom(rawBody).text : rawBody; const bodyLines = splitLines(body); const endLine = startLine + bodyLines.length - 1; const maxLine = Math.max(endLine, options.totalLineCount ?? 0); const width = String(maxLine).length; // Surfaced byte-level state: CRLF is detectable from any visible line, but a // missing trailing newline can only be asserted when the true end of file is // visible (i.e. the read result is not truncated beyond its shown content). const eofVisible = !(noticeMatch !== null && hasRealContinuation); const hasTrailingNewline = eofVisible ? rawBody.endsWith("\n") : true; const note = bytePropertiesNote(detectLineEnding(rawBody), hasTrailingNewline, false); const noteLine = note ? `\n${note}` : ""; const numbered = bodyLines .map((line, index) => `${String(startLine + index).padStart(width, " ")}| ${line}`) .join("\n"); return numbered ? `${numbered}${noteLine}${suffix}` : suffix.trimStart(); } export function numberReadText(text: string, options: HashReadTextOptions = {}): string { if (text.startsWith("Read image file ")) return text; return numberReadLines(text, options); }