workspacer_register/
find_last_import_end_before_offset.rs1crate::ix!();
3
4pub fn find_last_import_end_before_offset(
5 parsed_file: &SourceFile,
6 earliest_offset: usize,
7) -> Option<usize> {
8 trace!(
9 "Entering find_last_import_end_before_offset with earliest_offset={}",
10 earliest_offset
11 );
12
13 let file_text = parsed_file.syntax().text().to_string();
14 let file_len = file_text.len();
15 let mut answer = None;
16
17 for item in parsed_file.items() {
18 if is_imports_line(&item) {
19 let rng = item.syntax().text_range();
20 let start: usize = rng.start().into();
21 let end: usize = rng.end().into();
22 trace!("Found an imports line from {}..{}", start, end);
23
24 let line_end = find_line_end(&file_text, start);
26 debug!(
27 "Computed line_end={}, earliest_offset={}, start={}",
28 line_end, earliest_offset, start
29 );
30
31 let final_end = if line_end <= earliest_offset {
35 trace!("Line ends on/before earliest_offset => final_end=line_end={}", line_end);
36 line_end
37 } else if start <= earliest_offset {
38 trace!(
39 "Line starts before or exactly at earliest_offset => partial overlap => final_end=line_end={}",
40 line_end
41 );
42 line_end
43 } else {
44 trace!(
45 "Line starts after earliest_offset => ignoring (start={})",
46 start
47 );
48 continue;
49 };
50
51 if answer.map_or(true, |prev| final_end > prev) {
52 debug!("Updating answer from {:?} to {}", answer, final_end);
53 answer = Some(final_end);
54 }
55 } else {
56 trace!("Not an imports line => skipping");
57 }
58 }
59
60 debug!("Result = {:?}", answer);
61 trace!("Exiting find_last_import_end_before_offset");
62 answer
63}
64
65fn find_line_end(text: &str, start_pos: usize) -> usize {
66 trace!("Entering find_line_end with start_pos={}", start_pos);
67 let mut pos = start_pos;
68
69 while pos < text.len() {
70 if text.as_bytes()[pos] == b'\n' {
71 debug!("Found newline at pos={}; returning pos+1", pos);
73 pos += 1;
74 break;
75 }
76 pos += 1;
77 }
78
79 trace!("Exiting find_line_end with result={}", pos);
80 pos
81}
82
83#[cfg(test)]
84mod test_find_last_import_end_before_offset {
85 use super::*;
86 use ra_ap_syntax::{Edition, SourceFile};
87
88 fn parse_source(input: &str) -> SourceFile {
90 SourceFile::parse(input, Edition::Edition2021).tree()
91 }
92
93 #[traced_test]
95 fn test_empty_file() {
96 let src = "";
97 let parsed_file = parse_source(src);
98
99 let result = find_last_import_end_before_offset(&parsed_file, 0);
100 assert!(
101 result.is_none(),
102 "Empty file => no imports => should return None"
103 );
104 }
105
106 #[traced_test]
108 fn test_no_import_lines() {
109 let src = r#"
110fn nothing_special() {}
111"#;
112 let parsed_file = parse_source(src);
113
114 let earliest_offset = src.find("fn nothing_special").unwrap(); let result = find_last_import_end_before_offset(&parsed_file, earliest_offset);
116
117 assert!(result.is_none(), "No import lines => should return None");
118 }
119
120 #[traced_test]
122 fn test_single_import_line_before_earliest() {
123 let src = r#"
124#[macro_use] mod imports; use imports::*;
125
126fn something_else() {}
127"#;
128 let parsed_file = parse_source(src);
129
130 let earliest_offset = src.find("fn something_else").expect("missing fn");
132 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset)
133 .expect("Expected Some offset for the import line end");
134
135 assert!(
138 result < earliest_offset,
139 "Import line's end offset should be before earliest_offset"
140 );
141
142 assert_ne!(result, 0, "Should not be zero offset, we do have an import line");
144 }
145
146 #[traced_test]
149 fn test_import_line_ends_exactly_at_earliest() {
150 let src = r#"
151#[macro_use] mod imports; use imports::*;fn item(){}
152"#;
153 let parsed_file = parse_source(src);
158 let earliest_offset = src.find("fn item()").unwrap();
159 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset);
162 assert!(
163 result.is_some(),
164 "Import line that ends exactly at earliest_offset => recognized"
165 );
166 }
167
168 #[traced_test]
170 fn test_import_line_partially_overlaps_earliest() {
171 let src = r#"
175#[macro_use] mod imports; use imports::*; fn something() {}
176"#;
177 let parsed_file = parse_source(src);
178
179 let earliest_offset = src.find("fn something()").unwrap();
181 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset)
182 .expect("Should find the import line since it overlaps earliest");
183
184 assert!(
185 result > earliest_offset,
186 "Because the line extends beyond earliest_offset, the code includes it anyway"
187 );
188 }
189
190 #[traced_test]
192 fn test_multiple_imports_pick_greatest_end() {
193 let src = r#"
194#[macro_use] mod imports; use imports::*;
195#[macro_use] mod imports; use imports::*;
196fn item() {}
197"#;
198 let parsed_file = parse_source(src);
199
200 debug!("parsed_file: {:#?}", parsed_file);
201
202 let earliest_offset = src.find("fn item()").expect("missing fn item");
203
204 debug!("earliest_offset: {:#?}", earliest_offset);
205
206 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset)
207 .expect("We do have multiple imports lines");
208
209 debug!("result: {:#?}", result);
210
211 assert!(
215 result >= earliest_offset,
216 "Likely the second import line extends well beyond the item start"
217 );
218 }
219
220 #[traced_test]
222 fn test_import_line_after_earliest_ignored() {
223 let src = r#"
224fn real_item() {}
225
226#[macro_use] mod imports; use imports::*;
227"#;
228 let parsed_file = parse_source(src);
229
230 let earliest_offset = src.find("fn real_item()").unwrap();
232 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset);
233
234 assert!(result.is_none(), "Import line is after earliest => no recognized line");
236 }
237
238 #[traced_test]
240 fn test_random_non_import_lines_ignored() {
241 let src = r#"
242pub mod not_imports;
243use something_else;
244
245#[macro_use] mod imports; use imports::*;
246
247fn item() {}
248"#;
249 let parsed_file = parse_source(src);
252
253 let earliest_offset = src.find("fn item()").unwrap();
254 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset);
255
256 assert!(
257 result.is_some(),
258 "We do find the recognized import line near the end"
259 );
260 }
261
262 #[traced_test]
265 fn test_multiple_lines_partial_and_full_before() {
266 let src = r#"
267#[macro_use] mod imports; use imports::*;
268#[macro_use] mod imports; use imports::*; fn real_item(){}
269"#;
270 let parsed_file = parse_source(src);
271
272 let earliest_offset = src.find("fn real_item()").unwrap();
273 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset);
274
275 assert!(
281 result.is_some(),
282 "Should pick the second line's end because it partially overlaps"
283 );
284 }
285
286 #[traced_test]
288 fn test_pick_largest_end_among_several_candidates() {
289 let src = r#"
290#[macro_use] mod imports; use imports::*; // line 1
291#[macro_use] mod imports; use imports::*; // line 2 is presumably longer
292#[macro_use] mod imports; use imports::*; // line 3 is even longer
293fn item() {}
294"#;
295 let parsed_file = parse_source(src);
296 let earliest_offset = src.find("fn item()").unwrap();
297
298 let result = find_last_import_end_before_offset(&parsed_file, earliest_offset)
299 .expect("We have multiple import lines");
300
301 assert!(
306 result >= earliest_offset,
307 "Line 3 presumably overlaps or extends beyond earliest => it has the greatest end"
308 );
309 }
310}