Skip to main content

logicaffeine_language/lgx/
raw.rs

1//! Offsets inside a captured section body, mapped back to the file.
2//!
3//! The lexer captures a `View:`, `Head:` or `Style:` body as one raw block: its text with each
4//! line's indentation stripped, where its first character sits in the file, and how much
5//! indentation it stripped. Every stage that reports a position inside such a body — the template
6//! parser, the sub-lexed expressions, the lint, the stylesheet scoper — speaks in offsets into the
7//! dedented text, and [`RawMap`] is the one place those become file offsets.
8
9use crate::token::Span;
10
11/// A dedented raw body and where it came from.
12#[derive(Debug, Clone)]
13pub struct RawMap {
14    pub text: String,
15    /// The indentation stripped from every non-empty line after the first.
16    pub indent: usize,
17    /// The file offset of the body's first character.
18    pub content_start: usize,
19}
20
21impl RawMap {
22    pub fn new(text: impl Into<String>, indent: usize, content_start: usize) -> Self {
23        RawMap {
24            text: text.into(),
25            indent,
26            content_start,
27        }
28    }
29
30    /// The file offset of body offset `off`. Each line after the first is preceded by its newline
31    /// plus the indentation the capture stripped; a line that is EMPTY in the body came from an
32    /// empty line, which had none.
33    pub fn offset(&self, off: usize) -> usize {
34        let mut body_line_start = 0usize;
35        let mut file_line_start = self.content_start;
36        for (i, line) in self.text.split('\n').enumerate() {
37            if i > 0 {
38                file_line_start += 1 + if line.is_empty() { 0 } else { self.indent };
39            }
40            if off <= body_line_start + line.len() {
41                return file_line_start + (off - body_line_start);
42            }
43            body_line_start += line.len() + 1;
44            file_line_start += line.len();
45        }
46        file_line_start
47    }
48
49    /// A body span as a file span.
50    pub fn span(&self, s: Span) -> Span {
51        Span::new(self.offset(s.start), self.offset(s.end))
52    }
53}