Skip to main content

varar_core/
span.rs

1//! Source positions/ranges anchored to UTF-16 code-unit offsets (1-based
2//! line/column). Port of `varar-core/src/span.ts` / `Span.java`.
3
4/// A source range `[start_offset, end_offset)` in UTF-16 code units, with
5/// 1-based line/column at each end.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct Span {
8    pub start_offset: usize,
9    pub end_offset: usize,
10    pub start_line: usize,
11    pub start_col: usize,
12    pub end_line: usize,
13    pub end_col: usize,
14}
15
16/// A 1-based line/column position.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct LineCol {
19    pub line: usize,
20    pub col: usize,
21}
22
23impl Span {
24    /// Computes a [`Span`] for `[start_offset, end_offset)` (UTF-16 offsets) into `source`.
25    pub fn from_offsets(source: &str, start_offset: usize, end_offset: usize) -> Span {
26        let start = line_col(source, start_offset);
27        let end = line_col(source, end_offset);
28        Span {
29            start_offset,
30            end_offset,
31            start_line: start.line,
32            start_col: start.col,
33            end_line: end.line,
34            end_col: end.col,
35        }
36    }
37}
38
39/// Computes the 1-based (line, col) at `offset` (a UTF-16 code-unit index) into
40/// `source`. Walks per UTF-16 code unit from the start, exactly like Java's
41/// `charAt` loop (so an astral character advances `col` by 2).
42pub fn line_col(source: &str, offset: usize) -> LineCol {
43    let mut line = 1;
44    let mut col = 1;
45    for (idx, unit) in source.encode_utf16().enumerate() {
46        if idx >= offset {
47            break;
48        }
49        if unit == 0x000A {
50            line += 1;
51            col = 1;
52        } else {
53            col += 1;
54        }
55    }
56    LineCol { line, col }
57}