Skip to main content

LineIndex

Struct LineIndex 

Source
pub struct LineIndex<'src> { /* private fields */ }
Expand description

An index over a single source string that maps byte offsets to line/column coordinates and back.

A LineIndex is built once per source. Construction is a single linear scan that records the byte offset at which each line begins; after that, a forward lookup (line_col) is a binary search over those line starts — O(log lines) — followed by a character count within the one located line. Neither lookup direction allocates.

The index borrows the source rather than owning it: this crate maps positions and does not load text, so the caller keeps ownership of the buffer the index points into.

§Line endings

A line begins immediately after each \n. A \r\n sequence is therefore one line break, not two — the \r is the final character of the preceding line. A source with no trailing newline ends with a final line that has no terminator, and the empty string is one empty line. A lone \r not followed by \n is an ordinary character, not a line break, matching how language front-ends split source.

§Examples

use span_lang::{BytePos, LineCol, LineIndex};

let index = LineIndex::new("let x = 1;\nlet y = 2;\n");

// Forward: byte offset -> (line, column).
let lc = index.line_col(BytePos::new(11)); // first byte of line 2
assert_eq!(lc, LineCol::new(2, 1));

// Inverse: (line, column) -> byte offset.
assert_eq!(index.offset(lc), Some(BytePos::new(11)));

Implementations§

Source§

impl<'src> LineIndex<'src>

Source

pub fn new(src: &'src str) -> Self

Builds an index over src.

This is the only O(n) operation in the type; every subsequent lookup is sub-linear. src must be at most u32::MAX bytes — the addressing limit of BytePos — which holds for any single source a language front-end loads.

§Examples
use span_lang::LineIndex;

let index = LineIndex::new("one\ntwo\nthree");
assert_eq!(index.line_count(), 3);
Source

pub fn line_count(&self) -> usize

Returns the number of lines in the source.

This counts line starts: one, plus the number of \n bytes. The empty string is one line, and a trailing newline introduces a final empty line.

§Examples
use span_lang::LineIndex;

assert_eq!(LineIndex::new("").line_count(), 1);
assert_eq!(LineIndex::new("a\nb").line_count(), 2);
assert_eq!(LineIndex::new("a\nb\n").line_count(), 3);
Source

pub fn line_col(&self, pos: BytePos) -> LineCol

Resolves a byte offset to a 1-based LineCol.

The line is found by binary search over the recorded line starts in O(log lines); the column is the number of characters between the start of that line and pos, plus one.

Resolution is total and never panics. An offset past the end of the source is treated as the end, and an offset that falls inside a multi-byte character is rounded down to the start of that character — so the returned coordinate is always a real position in the source.

§Examples
use span_lang::{BytePos, LineCol, LineIndex};

let index = LineIndex::new("αβγ\nδε");
// The column counts characters, so γ is column 3 despite being byte 4.
assert_eq!(index.line_col(BytePos::new(4)), LineCol::new(1, 3));

// Past-the-end clamps to the final position rather than panicking.
assert_eq!(index.line_col(BytePos::new(9_999)), index.line_col(BytePos::new(11)));
Source

pub fn offset(&self, line_col: LineCol) -> Option<BytePos>

Resolves a 1-based LineCol back to a byte offset.

Returns None if the coordinate does not exist in the source: a line or column of 0, a line past the last, or a column past the end of its line. This is the inverse of line_col — for every valid byte position, resolving forward and then back returns the original offset.

§Examples
use span_lang::{BytePos, LineCol, LineIndex};

let index = LineIndex::new("αβ\nγδ");
// Line 2, column 2 is the second character of the second line.
assert_eq!(index.offset(LineCol::new(2, 2)), Some(BytePos::new(7)));

// Coordinates outside the source resolve to `None`.
assert_eq!(index.offset(LineCol::new(0, 1)), None);
assert_eq!(index.offset(LineCol::new(9, 1)), None);
assert_eq!(index.offset(LineCol::new(1, 99)), None);
Source

pub fn line_span(&self, line: u32) -> Option<Span>

Returns the byte span of a 1-based line’s text, excluding its terminator.

The span slices the source to exactly the line’s content: the trailing \n — and a \r immediately before it, for a \r\n ending — is not included, so &src[start..end] is the text a diagnostic would underline. This is the lookup a renderer uses to print the offending line. Returns None if line is 0 or past the last line.

The line’s start is found in O(log lines); trimming the terminator inspects at most two bytes, so the whole operation is allocation-free and never re-scans the source.

§Examples
use span_lang::LineIndex;

let src = "first\r\nsecond\nthird";
let index = LineIndex::new(src);

let line2 = index.line_span(2).expect("line 2 exists");
assert_eq!(&src[line2.start().to_usize()..line2.end().to_usize()], "second");

// The final, unterminated line is covered too.
let line3 = index.line_span(3).expect("line 3 exists");
assert_eq!(&src[line3.start().to_usize()..line3.end().to_usize()], "third");

assert_eq!(index.line_span(0), None);
assert_eq!(index.line_span(99), None);

Trait Implementations§

Source§

impl<'src> Clone for LineIndex<'src>

Source§

fn clone(&self) -> LineIndex<'src>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'src> Debug for LineIndex<'src>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'src> Freeze for LineIndex<'src>

§

impl<'src> RefUnwindSafe for LineIndex<'src>

§

impl<'src> Send for LineIndex<'src>

§

impl<'src> Sync for LineIndex<'src>

§

impl<'src> Unpin for LineIndex<'src>

§

impl<'src> UnsafeUnpin for LineIndex<'src>

§

impl<'src> UnwindSafe for LineIndex<'src>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.