All files / utilities position.ts

100% Statements 15/15
100% Branches 4/4
100% Functions 2/2
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 49 50 51 52                                              15x   15x 874x 16x       15x 26x     26x 26x   26x 26x   26x 20x   6x       26x      
import * as vscode from 'vscode';
 
/**
 * Maps a 0-based character offset to a {@link vscode.Position}. Satisfied by
 * `vscode.TextDocument.positionAt` and by {@link createPositionMapper}.
 */
export type PositionMapper = (offset: number) => vscode.Position;
 
/**
 * Builds an offset → {@link vscode.Position} mapper for a string, mirroring
 * `vscode.TextDocument.positionAt`. This lets diagnostics be computed from raw
 * file content — as produced by the workspace indexer — without opening a
 * `vscode.TextDocument`, which is the dominant per-file cost during indexing.
 *
 * Lines are split on `\n`; a `\r` in a `\r\n` sequence counts as a character of
 * the preceding line, matching the raw offsets the lexer/parser produce over the
 * same string. Offsets are clamped to `[0, content.length]`.
 * @param content The document text.
 * @returns A function mapping a 0-based character offset to a position.
 */
export function createPositionMapper(content: string): PositionMapper {
	// Start offset of each line: line 0 starts at 0, every char after a '\n'
	// begins a new line.
	const lineStarts: number[] = [0];
 
	for (let i = 0; i < content.length; i++) {
		if (content.charCodeAt(i) === 10 /* \n */) {
			lineStarts.push(i + 1);
		}
	}
 
	return (offset: number): vscode.Position => {
		const clamped = Math.max(0, Math.min(offset, content.length));
 
		// Binary search for the last line whose start offset is <= clamped.
		let low = 0;
		let high = lineStarts.length - 1;
 
		while (low < high) {
			const mid = (low + high + 1) >> 1;
 
			if (lineStarts[mid] <= clamped) {
				low = mid;
			} else {
				high = mid - 1;
			}
		}
 
		return new vscode.Position(low, clamped - lineStarts[low]);
	};
}