@effected/source
Source-text positions as plain values: a line index that turns an offset into an LSP-style zero-based line and character and back, and the range, position and diagnostic shapes the @effected format packages share. No service, no platform, no effect, and no dependency at all.
Pre-
1.0.0. This package is part of the@effected/*kit, still in0.xdevelopment. It has no dependencies, not eveneffect.Stability: unstable. This package's API surface is not yet considered complete and may change across
0.xreleases. Pin an exact version. Full policy: release strategy.
Why @effected/source
A parser reports where something went wrong as an offset into the text. An editor, a language server and a person all want a line and a column. The conversion looks like a five-line loop, and that loop is easy to get subtly wrong: which characters end a line, what an offset between the \r and \n of a Windows line ending means, and whether a column counts code units or characters. This package is the conversion written once, by the rules the Language Server Protocol uses.
Install
npm install @effected/source
pnpm add @effected/source
Requires Node.js >=24.11.0. No dependencies and no peers. ESM-only: import it from an ES module.
Usage
import { LineIndex } from "@effected/source";
const index = LineIndex.make("name: kit\r\nversion: 1\n");
index.positionAt(11);
// { line: 1, character: 0 }
index.offsetAt({ line: 1, character: 9 });
// 20
index.spanOf({ offset: 11, length: 7 });
// { start: { line: 1, character: 0 }, end: { line: 1, character: 7 } }
LineIndex.make(text, options?)builds the line table once; every query after that is a binary search.positionAtandoffsetAtconvert both ways,lineCount,lineAt,lineStart,lineEndandlineTextdescribe the lines, andspanOf,rangeOfandlocationOfconvert ranges.- Positions are zero-based and
charactercounts UTF-16 code units, LSP's default. Add 1 to both for a person. - Lines end at
\n,\r\nor a bare\rby default.lineBreaks: "lf"ends them at\nonly, andlineBreaks: "unicode"also at U+2028 and U+2029. - Out-of-range input clamps to the nearest valid position, as
vscode-languageserver-textdocumentdoes. A value that is not an integer throws aRangeError. SourcePosition,SourceRange,SourceSpan,SourceLocationandSourceDiagnosticare structural interfaces, so the diagnostics and ranges of@effected/jsonc,@effected/yaml,@effected/tomland@effected/markdowncan be read through them as they are.
Documentation
The guide and the API reference are at effected.spencerbeg.gs/source.