lib_ruby_parser/
loc_ext.rs

1use crate::source::DecodedInput;
2pub use lib_ruby_parser_ast::Loc;
3
4/// A set of parser-specific extensions for `Loc`
5pub trait LocExt {
6    /// Returns line and column of the `begin` of the `Loc` on a given `Input`
7    fn begin_line_col(&self, input: &DecodedInput) -> Option<(usize, usize)>;
8    /// Expands `Loc` to the whole line and returns line number and new `Loc`
9    fn expand_to_line(&self, input: &DecodedInput) -> Option<(usize, Loc)>;
10    /// Returns source code of the current `Loc` on a given `Input`
11    fn source(&self, input: &DecodedInput) -> Option<String>;
12}
13
14impl LocExt for Loc {
15    fn begin_line_col(&self, input: &DecodedInput) -> Option<(usize, usize)> {
16        input.line_col_for_pos(self.begin)
17    }
18
19    fn expand_to_line(&self, input: &DecodedInput) -> Option<(usize, Loc)> {
20        let (begin_line, _) = self.begin_line_col(input)?;
21        let line_no = begin_line;
22        let line = input.line_at(line_no);
23        Some((
24            line_no,
25            Self {
26                begin: line.start,
27                end: line.line_end(),
28            },
29        ))
30    }
31
32    /// Returns source code of the current `Loc` on a given `Input`
33    fn source(&self, input: &DecodedInput) -> Option<String> {
34        let bytes = input.substr_at(self.begin, self.end)?;
35        Some(String::from_utf8_lossy(bytes).into_owned())
36    }
37}
38
39// impl Loc {
40//     /// Converts location to a range
41//     pub fn to_range(&self) -> std::ops::Range<usize> {
42//         self.begin..self.end
43//     }
44
45//     /// Returns size of the `Loc` (i.e. `end - begin`)
46//     pub fn size(&self) -> usize {
47//         self.end - self.begin
48//     }
49
50//     /// Returns a new `Loc` with given `begin` and current `end`
51//     pub fn with_begin(&self, begin: usize) -> Loc {
52//         Self {
53//             begin,
54//             end: self.end,
55//         }
56//     }
57
58//     /// Returns a new `Loc` with given `end` and current `begin`
59//     pub fn with_end(&self, end: usize) -> Loc {
60//         Self {
61//             begin: self.begin,
62//             end,
63//         }
64//     }
65
66//     /// Adds given `delta` to `begin`
67//     pub fn adjust_begin(&self, delta: i32) -> Loc {
68//         let begin: i32 = self
69//             .begin
70//             .try_into()
71//             .expect("failed to convert location to i32 (is it too big?)");
72//         let begin: usize = (begin + delta)
73//             .try_into()
74//             .expect("failed to convert location to usize (is it negative?)");
75//         Self {
76//             begin,
77//             end: self.end,
78//         }
79//     }
80
81//     /// Adds given `delta` to `end`
82//     pub fn adjust_end(&self, d: i32) -> Loc {
83//         let end: i32 = self
84//             .end
85//             .try_into()
86//             .expect("failed to convert location to i32 (is it too big?)");
87//         let end: usize = (end + d)
88//             .try_into()
89//             .expect("failed to convert location to usize (is it negative?)");
90//         Self {
91//             begin: self.begin,
92//             end,
93//         }
94//     }
95
96//     /// Returns a new `Loc` with the same `begin`, but adjusted `end`,
97//     /// so that its size is equal to given `new_size`
98//     pub fn resize(&self, new_size: usize) -> Loc {
99//         self.with_end(self.begin + new_size)
100//     }
101
102//     /// Joins two `Loc`s by choosing `min(begin)` + `max(end)`
103//     pub fn join(&self, other: &Self) -> Loc {
104//         Self {
105//             begin: std::cmp::min(self.begin, other.begin),
106//             end: std::cmp::max(self.end, other.end),
107//         }
108//     }
109
110//     pub(crate) fn maybe_join(&self, other: &Option<Loc>) -> Loc {
111//         match other.as_ref() {
112//             Some(other) => self.join(other),
113//             None => *self,
114//         }
115//     }
116
117//     /// Returns true if `Loc` is empty (i.e. `begin` == `end`)
118//     pub fn is_empty(&self) -> bool {
119//         self.begin == self.end
120//     }
121
122//     pub(crate) fn begin_line_col(&self, input: &DecodedInput) -> Option<(usize, usize)> {
123//         input.line_col_for_pos(self.begin)
124//     }
125
126//     pub(crate) fn expand_to_line(&self, input: &DecodedInput) -> Option<(usize, Loc)> {
127//         let (begin_line, _) = self.begin_line_col(input)?;
128//         let line_no = begin_line;
129//         let line = input.line_at(line_no);
130//         Some((
131//             line_no,
132//             Self {
133//                 begin: line.start,
134//                 end: line.line_end(),
135//             },
136//         ))
137//     }
138
139//     /// Returns source code of the current `Loc` on a given `Input`
140//     pub fn source(&self, input: &DecodedInput) -> Option<String> {
141//         let bytes = input.substr_at(self.begin, self.end)?;
142//         Some(String::from_utf8_lossy(bytes).into_owned())
143//     }
144
145//     pub(crate) fn print(&self, name: &str) {
146//         println!(
147//             "{}{} {}",
148//             " ".repeat(self.begin),
149//             "~".repeat(self.size()),
150//             name
151//         )
152//     }
153// }
154
155// impl std::fmt::Debug for Loc {
156//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157//         f.write_str(&format!("{}...{}", self.begin, self.end))
158//     }
159// }
160
161// #[test]
162// fn test_to_range() {
163//     assert_eq!(Loc { begin: 10, end: 20 }.to_range(), 10..20)
164// }
165
166// #[test]
167// fn test_fmt() {
168//     assert_eq!(format!("{:?}", Loc { begin: 10, end: 20 }), "10...20")
169// }
170
171// #[test]
172// fn test_is_empty() {
173//     assert!(Loc { begin: 1, end: 1 }.is_empty());
174//     assert!(!Loc { begin: 1, end: 2 }.is_empty());
175// }