UNPKG

@pierre/diffs

Version:

80 lines (79 loc) 2.78 kB
//#region src/utils/computeFileOffsets.ts const LINE_FEED = 10; const CARRIAGE_RETURN = 13; const DENSE_LINE_BREAK_BLOCK_SIZE = 4; const DENSE_LINE_BREAK_BLOCK_SPAN = 40; /** * Computes line start offsets for a string. */ function computeLineOffsets(contents) { const offsets = [0]; let carriageReturn = contents.indexOf("\r"); let lineFeed = contents.indexOf("\n"); let blockStart = 0; let blockBreaks = 0; while (carriageReturn !== -1 || lineFeed !== -1) { let nextOffset; if (carriageReturn !== -1 && (lineFeed === -1 || carriageReturn < lineFeed)) if (lineFeed === carriageReturn + 1) { nextOffset = lineFeed + 1; carriageReturn = contents.indexOf("\r", nextOffset); lineFeed = contents.indexOf("\n", nextOffset); } else { nextOffset = carriageReturn + 1; carriageReturn = contents.indexOf("\r", nextOffset); } else { nextOffset = lineFeed + 1; lineFeed = contents.indexOf("\n", nextOffset); } offsets.push(nextOffset); if (++blockBreaks === DENSE_LINE_BREAK_BLOCK_SIZE) { if (nextOffset - blockStart <= DENSE_LINE_BREAK_BLOCK_SPAN) { for (let i = nextOffset; i < contents.length; i++) { const char = contents.charCodeAt(i); if (char === LINE_FEED || char === CARRIAGE_RETURN) { if (char === CARRIAGE_RETURN && i + 1 < contents.length && contents.charCodeAt(i + 1) === LINE_FEED) i++; offsets.push(i + 1); } } return offsets; } blockStart = nextOffset; blockBreaks = 0; } } return offsets; } /** * Counts line breaks in a string, treating `\n`, `\r`, and `\r\n` the same way * {@link computeLineOffsets} does (a `\r\n` pair is one break). Mirrors that * scan but counts in a single pass instead of building and discarding an * offsets array, so sizing the changed-line range for large edits stays cheap. * A unit test asserts it stays in lockstep with `computeLineOffsets`. */ function countLineBreaks(contents) { let count = 0; for (let i = 0; i < contents.length; i++) { const char = contents.charCodeAt(i); if (char === LINE_FEED || char === CARRIAGE_RETURN) { if (char === CARRIAGE_RETURN && i + 1 < contents.length && contents.charCodeAt(i + 1) === LINE_FEED) i++; count++; } } return count; } /** * Splits file contents into lines aligned with {@link computeLineOffsets}. * Unlike splitFileContents, a trailing newline produces a final empty line. */ function linesFromFileContents(contents) { const offsets = computeLineOffsets(contents); return Array.from({ length: offsets.length }, (_, i) => { const start = offsets[i]; const end = offsets[i + 1] ?? contents.length; return contents.slice(start, end); }); } //#endregion export { computeLineOffsets, countLineBreaks, linesFromFileContents }; //# sourceMappingURL=computeFileOffsets.js.map