1use std::{
4 convert::{TryFrom, TryInto},
5 fmt::Display,
6};
7
8use text_size::{TextRange, TextSize};
9
10use crate::FileId;
11
12#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(rserde::Serialize, rserde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(crate = "rserde"))]
16pub struct LexerPosition {
17 pub source_id: FileId,
19 pub offset: TextSize,
21}
22
23impl LexerPosition {
24 pub fn new(source_id: FileId, offset: TextSize) -> Self {
31 Self { source_id, offset }
32 }
33
34 pub fn new_raw<E: std::fmt::Debug>(
45 source_id: FileId,
46 offset: impl TryInto<u32, Error = E>,
47 ) -> Self {
48 Self {
49 source_id,
50 offset: offset.try_into().expect("input too large").into(),
51 }
52 }
53}
54
55impl Display for LexerPosition {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "{}:{}", self.source_id, u32::from(self.offset))
58 }
59}
60
61impl From<LexerPosition> for FileId {
62 fn from(value: LexerPosition) -> Self {
63 value.source_id
64 }
65}
66
67impl From<LexerPosition> for TextSize {
68 fn from(value: LexerPosition) -> Self {
69 value.offset
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75#[cfg_attr(feature = "serde", derive(rserde::Serialize, rserde::Deserialize))]
76#[cfg_attr(feature = "serde", serde(crate = "rserde"))]
77pub struct NodeSpan {
78 source_id: FileId,
81
82 range: TextRange,
84}
85
86impl NodeSpan {
87 pub fn new(source_id: FileId, range: TextRange) -> Self {
89 Self { source_id, range }
90 }
91
92 pub fn from_lexer(start: LexerPosition, end: LexerPosition) -> Self {
94 Self {
95 source_id: start.source_id,
96 range: TextRange::new(start.offset, end.offset),
97 }
98 }
99
100 pub fn new_start(source_id: FileId) -> Self {
104 Self {
105 source_id,
106 range: TextRange::default(),
107 }
108 }
109
110 pub fn new_end(source_id: FileId, length: usize) -> Self {
114 let length = TextSize::try_from(length).expect("length is too large");
115
116 Self {
117 source_id,
118 range: TextRange::new(length, length),
119 }
120 }
121
122 pub fn source_id(&self) -> FileId {
124 self.source_id
125 }
126
127 pub fn range(&self) -> TextRange {
129 self.range
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.range.is_empty()
135 }
136
137 pub fn len(&self) -> TextSize {
139 self.range.len()
140 }
141
142 pub fn start(&self) -> LexerPosition {
144 LexerPosition::new(self.source_id, self.range.start())
145 }
146
147 pub fn end(&self) -> LexerPosition {
149 LexerPosition::new(self.source_id, self.range.end())
150 }
151}
152
153impl PartialOrd for NodeSpan {
154 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
155 Some(self.cmp(other))
156 }
157}
158
159impl Ord for NodeSpan {
160 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
161 self.source_id
162 .cmp(&other.source_id)
163 .then(self.range.start().cmp(&other.range.start()))
164 .then(self.range.len().cmp(&other.range.len()))
165 }
166}
167
168impl From<NodeSpan> for FileId {
169 fn from(value: NodeSpan) -> Self {
170 value.source_id
171 }
172}
173
174impl From<NodeSpan> for TextRange {
175 fn from(value: NodeSpan) -> Self {
176 value.range
177 }
178}