Skip to main content

mq_lang/
range.rs

1use nom_locate::LocatedSpan;
2
3use crate::module::ModuleId;
4#[cfg(feature = "ast-json")]
5use serde::{Deserialize, Serialize};
6
7/// A span representing a location in source code with module context.
8///
9/// This type combines nom's `LocatedSpan` with a module identifier,
10/// enabling accurate source location tracking across multiple modules.
11pub type Span<'a> = LocatedSpan<&'a str, ModuleId>;
12
13/// A position in source code, representing a line and column.
14#[cfg_attr(feature = "ast-json", derive(Serialize, Deserialize))]
15#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash)]
16pub struct Position {
17    pub line: u32,
18    pub column: usize,
19}
20
21impl Default for Position {
22    fn default() -> Self {
23        Position { line: 1, column: 1 }
24    }
25}
26
27impl Position {
28    /// Creates a new position with the specified line and column.
29    pub fn new(line: u32, column: usize) -> Self {
30        Position { line, column }
31    }
32}
33
34/// A range in source code, spanning from a start position to an end position.
35#[cfg_attr(feature = "ast-json", derive(Serialize, Deserialize))]
36#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Default, Hash)]
37pub struct Range {
38    pub start: Position,
39    pub end: Position,
40}
41
42impl Range {
43    /// Returns `true` if the specified position falls within this range (inclusive).
44    pub fn contains(&self, position: &Position) -> bool {
45        (self.start.line < position.line || (self.start.line == position.line && self.start.column <= position.column))
46            && (self.end.line > position.line || (self.end.line == position.line && self.end.column >= position.column))
47    }
48}
49
50impl<'a> From<Span<'a>> for Range {
51    fn from(span: Span<'a>) -> Self {
52        let fragment = span.fragment();
53        let fragment = if !fragment.starts_with(" ") && fragment.ends_with(" ") {
54            fragment.trim()
55        } else {
56            fragment
57        };
58
59        Range {
60            start: Position {
61                line: span.location_line(),
62                column: span.get_utf8_column(),
63            },
64            end: Position {
65                line: span.location_line(),
66                column: span.get_utf8_column() + fragment.chars().count(),
67            },
68        }
69    }
70}
71
72impl<'a> From<Span<'a>> for Position {
73    fn from(span: Span<'a>) -> Self {
74        Position {
75            line: span.location_line(),
76            column: span.get_utf8_column(),
77        }
78    }
79}