export class SpanResolver {
constructor(content, normalizeLineEndings = true) {
this.normalizeLineEndings = normalizeLineEndings;
this.text = normalizeLineEndings ? content.replace(/\r\n/g, '\n') : content;
this.lineOffsets = [0]; for (let i = 0; i < this.text.length; i++) {
if (this.text[i] === '\n') {
this.lineOffsets.push(i + 1);
}
}
}
getText() {
return this.text;
}
byteToLineCol(byteOffset) {
const clampedOffset = Math.max(0, Math.min(byteOffset, this.text.length));
if (this.text.length === 0) {
return { line: 1, col: 1 };
}
let line = 1;
let lineStart = 0;
for (let i = this.lineOffsets.length - 1; i >= 0; i--) {
const offset = this.lineOffsets[i];
if (offset !== undefined && clampedOffset >= offset) {
line = i + 1;
lineStart = offset;
break;
}
}
const colOffset = clampedOffset - lineStart;
const lineContent = this.text.substring(lineStart, lineStart + colOffset);
const col = Array.from(lineContent).length + 1;
return { line, col };
}
resolveSpan(startOffset, endOffset) {
const start = this.byteToLineCol(startOffset);
const end = this.byteToLineCol(endOffset);
return { start, end };
}
}