gobject_ast/model/
source_location.rs1use std::{fmt, sync::Arc};
2
3use serde::{Serialize, Serializer};
4
5#[derive(Clone, Default)]
7pub struct SourceLocation {
8 pub line: usize,
9 pub column: usize,
10 pub start_byte: usize,
11 pub end_byte: usize,
12 source: Arc<Vec<u8>>,
13}
14
15impl fmt::Debug for SourceLocation {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 write!(f, "{}:{}", self.line, self.column)
18 }
19}
20
21impl Serialize for SourceLocation {
22 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
23 s.serialize_str(&format!("{}:{}", self.line, self.column))
24 }
25}
26
27impl SourceLocation {
28 pub fn new(
29 line: usize,
30 column: usize,
31 start_byte: usize,
32 end_byte: usize,
33 source: Arc<Vec<u8>>,
34 ) -> Self {
35 Self {
36 line,
37 column,
38 start_byte,
39 end_byte,
40 source,
41 }
42 }
43
44 pub fn with_byte_range(&self, start_byte: usize, end_byte: usize) -> Self {
48 Self {
49 line: 0,
50 column: 0,
51 start_byte,
52 end_byte,
53 source: Arc::clone(&self.source),
54 }
55 }
56
57 pub fn source(&self) -> &[u8] {
59 &self.source
60 }
61
62 pub fn as_str(&self) -> Option<&str> {
64 std::str::from_utf8(&self.source[self.start_byte..self.end_byte]).ok()
65 }
66
67 fn find_line_start(&self) -> usize {
69 let mut line_start = self.start_byte;
70 while line_start > 0 && self.source[line_start - 1] != b'\n' {
71 line_start -= 1;
72 }
73 line_start
74 }
75
76 fn find_line_end(&self) -> usize {
79 let mut pos = self.end_byte;
80 while pos < self.source.len() && self.source[pos] != b'\n' {
81 pos += 1;
82 }
83 if pos < self.source.len() {
84 pos += 1;
85 }
86 pos
87 }
88
89 pub fn find_line_range(&self) -> (usize, usize) {
92 (self.find_line_start(), self.find_line_end())
93 }
94
95 pub fn find_line_bounds(&self) -> (usize, usize) {
100 let mut line_start = self.find_line_start();
102
103 if line_start > 0 {
105 let mut prev_line_start = line_start - 1; while prev_line_start > 0 && self.source[prev_line_start - 1] != b'\n' {
107 prev_line_start -= 1;
108 }
109
110 let prev_line = &self.source[prev_line_start..line_start - 1];
112 if prev_line.iter().all(|&b| b == b' ' || b == b'\t') {
113 line_start = prev_line_start;
114 }
115 }
116
117 let mut line_end = self.start_byte;
119 while line_end < self.source.len() && self.source[line_end] != b'\n' {
120 line_end += 1;
121 }
122 if line_end < self.source.len() && self.source[line_end] == b'\n' {
124 line_end += 1;
125 }
126
127 (line_start, line_end)
128 }
129
130 pub fn find_line_bounds_with_following_blank(&self) -> (usize, usize) {
134 let line_start = self.find_line_start();
135 let mut line_end = self.find_line_end();
136
137 if line_end < self.source.len() {
139 let mut pos = line_end;
140 let mut is_blank = true;
141
142 while pos < self.source.len() && self.source[pos] != b'\n' {
143 if !self.source[pos].is_ascii_whitespace() {
144 is_blank = false;
145 break;
146 }
147 pos += 1;
148 }
149
150 if is_blank && pos < self.source.len() {
152 line_end = pos + 1; }
154 }
155
156 (line_start, line_end)
157 }
158
159 pub fn extract_line_indentation(&self) -> String {
162 let line_start = self.find_line_start();
163
164 let mut indent = String::new();
166 let mut i = line_start;
167 while i < self.source.len() && (self.source[i] == b' ' || self.source[i] == b'\t') {
168 indent.push(self.source[i] as char);
169 i += 1;
170 }
171
172 indent
173 }
174
175 pub fn extract_indentation(&self) -> String {
179 let line_start = self.find_line_start();
180
181 let mut indent = String::new();
184 for &byte in &self.source[line_start..self.start_byte] {
185 if byte == b' ' || byte == b'\t' {
186 indent.push(byte as char);
187 } else {
188 break;
189 }
190 }
191
192 indent
193 }
194
195 pub fn find_after(&self, target: u8) -> usize {
199 let mut pos = self.end_byte;
200 while pos < self.source.len() {
201 if self.source[pos] == target {
202 return pos + 1;
203 }
204 if !self.source[pos].is_ascii_whitespace() {
205 break;
206 }
207 pos += 1;
208 }
209 self.end_byte
210 }
211
212 pub fn find_before(&self, target: u8) -> usize {
216 let mut pos = self.start_byte;
217 while pos > 0 && self.source[pos - 1].is_ascii_whitespace() {
218 pos -= 1;
219 }
220 if pos > 0 && self.source[pos - 1] == target {
221 pos -= 1;
222 while pos > 0 && self.source[pos - 1] == b' ' {
224 pos -= 1;
225 }
226 }
227 pos
228 }
229
230 pub fn find_semicolon_end(&self) -> usize {
232 self.find_after(b';')
233 }
234
235 pub fn count_trailing_newlines(&self) -> usize {
237 self.source[self.end_byte..]
238 .iter()
239 .take_while(|&&b| b == b'\n')
240 .count()
241 }
242
243 pub fn find_braces_around(&self) -> (usize, usize) {
247 let source = &self.source;
248 let start = self.start_byte;
249
250 let mut brace_start = start;
252 while brace_start > 0 && source[brace_start - 1] != b'{' {
253 brace_start -= 1;
254 if start - brace_start > 100 {
255 break;
256 }
257 }
258 if brace_start > 0 && source[brace_start - 1] == b'{' {
259 brace_start -= 1;
260 }
261
262 let mut brace_end = brace_start + 1;
265 let mut depth = 1;
266 while brace_end < source.len() && depth > 0 {
267 if source[brace_end] == b'{' {
268 depth += 1;
269 } else if source[brace_end] == b'}' {
270 depth -= 1;
271 }
272 brace_end += 1;
273 }
274
275 (brace_start, brace_end)
276 }
277}