export type SerializedPosition = { line: number column: number } export class Position { readonly line: number readonly column: number static from(position: SerializedPosition): Position static from(line: number, column: number): Position static from(positionOrLine: SerializedPosition | number, column?: number): Position { if (typeof positionOrLine === "number") { return new Position(positionOrLine, column!) } else { return new Position(positionOrLine.line, positionOrLine.column) } } static get zero() { return new Position(0, 0) } constructor(line: number, column: number) { this.line = line this.column = column } toHash(): SerializedPosition { return { line: this.line, column: this.column } } toJSON(): SerializedPosition { return this.toHash() } treeInspect(): string { return `(${this.line}:${this.column})` } inspect(): string { return `#` } toString(): string { return this.inspect() } } /** * Converts a character offset in a source string to a Position (line, column). * Lines are 1-based, columns are 0-based. * * @param source - The source string the offset is relative to * @param offset - A UTF-16 string index into `source` * @returns The Position at `offset` */ export function positionFromOffset(source: string, offset: number): Position { let line = 1 let column = 0 let currentOffset = 0 for (let i = 0; i < source.length && currentOffset < offset; i++) { const char = source[i] currentOffset++ if (char === "\n") { line++ column = 0 } else { column++ } } return new Position(line, column) }