1use crate::{Metrics, Span};
2use std::fmt;
3
4#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
22pub struct Position {
23 pub line: usize,
25
26 pub column: usize,
28}
29
30impl Position {
31 #[must_use]
35 pub const fn new(line: usize, column: usize) -> Self { Self { line, column } }
36
37 #[must_use]
50 pub const fn end() -> Self {
51 Self {
52 line: usize::max_value(),
53 column: usize::max_value(),
54 }
55 }
56
57 #[must_use]
59 pub const fn next_column(&self) -> Self {
60 Self {
61 line: self.line,
62 column: self.column + 1,
63 }
64 }
65
66 #[must_use]
68 pub const fn reset_column(&self) -> Self {
69 Self {
70 line: self.line,
71 column: 0,
72 }
73 }
74
75 #[must_use]
77 pub const fn next_line(&self) -> Self {
78 Self {
79 line: self.line + 1,
80 column: 0,
81 }
82 }
83
84 #[must_use]
112 pub fn next<M: Metrics>(&self, c: char, metrics: &M) -> Self {
113 match c {
114 '\n' => self.next_line(),
115 '\t' => {
116 let ts = metrics.tab_stop();
117 Self {
118 line: self.line,
119 column: (self.column / ts) * ts + ts,
120 }
121 }
122 c if c.is_control() => *self,
123 _ => {
124 Self {
125 line: self.line,
126 column: self.column + metrics.char_width(c),
127 }
128 }
129 }
130 }
131
132 pub fn shift<M: Metrics>(&mut self, c: char, metrics: &M) { *self = self.next(c, metrics) }
133
134 #[must_use]
140 #[inline(always)]
141 pub fn from(&self, first: Self, last: Self) -> Span {
142 Span::new(first, last, *self)
143 }
144
145 #[must_use]
151 #[inline(always)]
152 pub fn from_included(&self, first: Self, end: Self) -> Span {
153 Span::new(first, *self, end)
154 }
155
156 #[must_use]
162 #[inline(always)]
163 pub fn to(&self, last: Self, end: Self) -> Span {
164 Span::new(*self, last, end)
165 }
166}
167
168impl fmt::Display for Position {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 if self.line == usize::max_value() && self.column == usize::max_value() {
171 write!(f, "line [end] column [end]")
172 } else if self.line == usize::max_value() {
173 write!(f, "line [end] column {}", self.column + 1)
174 } else if self.column == usize::max_value() {
175 write!(f, "line {} column [end]", self.line + 1)
176 } else {
177 write!(f, "line {} column {}", self.line + 1, self.column + 1)
178 }
179 }
180}
181
182impl fmt::Debug for Position {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 if self.line == usize::max_value() && self.column == usize::max_value() {
185 write!(f, "[end]:[end]")
186 } else if self.line == usize::max_value() {
187 write!(f, "[end]:{}", self.column + 1)
188 } else if self.column == usize::max_value() {
189 write!(f, "{}:[end]", self.line + 1)
190 } else {
191 write!(f, "{}:{}", self.line + 1, self.column + 1)
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 macro_rules! min {
201 ($x: expr) => ($x);
202 ($x: expr, $($z: expr),+ $(,)* ) => (::std::cmp::min($x, min!($($z),*)));
203 }
204
205 macro_rules! max {
206 ($x: expr) => ($x);
207 ($x: expr, $($z: expr),+ $(,)* ) => (::std::cmp::max($x, max!($($z),*)));
208 }
209
210 #[test]
215 fn test_ord_position() {
216 assert_eq!(
217 min!(
218 Position::new(1, 2),
219 Position::new(1, 3),
220 Position::new(1, 4),
221 Position::new(1, 2),
222 Position::new(2, 1),
223 Position::new(3, 12),
224 Position::new(4, 4),
225 ),
226 Position::new(1, 2)
227 );
228
229 assert_eq!(
230 max!(
231 Position::new(1, 2),
232 Position::new(1, 3),
233 Position::new(1, 4),
234 Position::new(1, 2),
235 Position::new(2, 1),
236 Position::new(3, 12),
237 Position::new(4, 4),
238 ),
239 Position::new(4, 4)
240 );
241 }
242
243 #[test]
244 fn test_debug() {
245 assert_eq!(format!("{:?}", Position::new(2, 3)), "3:4".to_string());
246 assert_eq!(
247 format!("{:?}", Position::new(usize::max_value(), 3)),
248 "[end]:4".to_string()
249 );
250 assert_eq!(
251 format!("{:?}", Position::new(3, usize::max_value())),
252 "4:[end]".to_string()
253 );
254 assert_eq!(
255 format!(
256 "{:?}",
257 Position::new(usize::max_value(), usize::max_value())
258 ),
259 "[end]:[end]".to_string()
260 );
261 }
262}