1use std::cmp::PartialEq;
2use std::mem::take;
3
4use itertools::Itertools;
5use sqruff_lib_core::dialects::syntax::SyntaxKind;
6use sqruff_lib_core::lint_fix::LintFix;
7use sqruff_lib_core::parser::segments::{ErasedSegment, Tables};
8
9use super::config::ReflowConfig;
10use super::depth_map::DepthMap;
11use super::elements::{ReflowBlock, ReflowElement, ReflowPoint, ReflowSequenceType};
12use super::rebreak::rebreak_sequence;
13use super::reindent::{construct_single_indent, lint_indent_points, lint_line_length};
14use crate::core::config::FluffConfig;
15use crate::core::rules::LintResult;
16
17pub struct ReflowSequence<'a> {
18 root_segment: ErasedSegment,
19 elements: ReflowSequenceType,
20 lint_results: Vec<LintResult>,
21 reflow_config: &'a ReflowConfig,
22 depth_map: DepthMap,
23}
24
25#[derive(Clone, Copy, PartialEq, Eq)]
26pub enum TargetSide {
27 Both,
28 Before,
29 After,
30}
31
32#[derive(Clone, Copy, PartialEq, Eq)]
33pub enum ReflowInsertPosition {
34 Before,
35}
36
37impl<'a> ReflowSequence<'a> {
38 pub fn raw(&self) -> String {
39 self.elements.iter().map(|it| it.raw()).join("")
40 }
41
42 pub fn results(self) -> Vec<LintResult> {
43 self.lint_results
44 }
45
46 pub fn fixes(self) -> Vec<LintFix> {
47 self.results()
48 .into_iter()
49 .flat_map(|result| result.fixes)
50 .collect()
51 }
52
53 pub fn from_root(root_segment: ErasedSegment, config: &'a FluffConfig) -> Self {
54 let depth_map = DepthMap::from_parent(&root_segment).into();
55
56 Self::from_raw_segments(
57 root_segment.get_raw_segments(),
58 root_segment,
59 config,
60 depth_map,
61 )
62 }
63
64 pub fn from_raw_segments(
65 segments: Vec<ErasedSegment>,
66 root_segment: ErasedSegment,
67 config: &'a FluffConfig,
68 depth_map: Option<DepthMap>,
69 ) -> ReflowSequence<'a> {
70 let reflow_config = config.reflow();
71 let depth_map = depth_map.unwrap_or_else(|| {
72 DepthMap::from_raws_and_root(segments.clone().into_iter(), &root_segment)
73 });
74 let elements = Self::elements_from_raw_segments(segments, &depth_map, reflow_config);
75
76 Self {
77 root_segment,
78 elements,
79 lint_results: Vec::new(),
80 reflow_config,
81 depth_map,
82 }
83 }
84
85 fn elements_from_raw_segments(
86 segments: Vec<ErasedSegment>,
87 depth_map: &DepthMap,
88 reflow_config: &ReflowConfig,
89 ) -> Vec<ReflowElement> {
90 let mut elem_buff = Vec::new();
91 let mut seg_buff = Vec::new();
92
93 for seg in segments {
94 if matches!(
99 seg.get_type(),
100 SyntaxKind::Whitespace
101 | SyntaxKind::Newline
102 | SyntaxKind::Indent
103 | SyntaxKind::Implicit
104 | SyntaxKind::Dedent
105 ) {
106 seg_buff.push(seg);
108 continue;
109 } else if !elem_buff.is_empty() || !seg_buff.is_empty() {
110 let seg_buff = take(&mut seg_buff);
113 elem_buff.push(ReflowElement::Point(ReflowPoint::new(seg_buff)));
114 }
115
116 let depth_info = depth_map.get_depth_info(&seg);
118 elem_buff.push(ReflowElement::Block(ReflowBlock::from_config(
119 seg,
120 reflow_config,
121 depth_info,
122 )));
123 }
124
125 if !seg_buff.is_empty() {
126 elem_buff.push(ReflowPoint::new(seg_buff).into());
127 }
128
129 elem_buff
130 }
131
132 pub fn from_around_target(
133 target_segment: &ErasedSegment,
134 root_segment: ErasedSegment,
135 sides: TargetSide,
136 config: &'a FluffConfig,
137 ) -> ReflowSequence<'a> {
138 let all_raws = root_segment.get_raw_segments();
139 let target_raws = target_segment.get_raw_segments();
140
141 assert!(!target_raws.is_empty());
142
143 let pre_idx = all_raws.iter().position(|x| x == &target_raws[0]).unwrap();
144 let post_idx = all_raws
145 .iter()
146 .position(|x| x == &target_raws[target_raws.len() - 1])
147 .unwrap()
148 + 1;
149
150 let mut pre_idx = pre_idx;
151 let mut post_idx = post_idx;
152
153 if sides == TargetSide::Both || sides == TargetSide::Before {
154 pre_idx -= 1;
155 for i in (0..=pre_idx).rev() {
156 if all_raws[i].is_code() {
157 pre_idx = i;
158 break;
159 }
160 }
161 }
162
163 if sides == TargetSide::Both || sides == TargetSide::After {
164 for (i, it) in all_raws.iter().enumerate().skip(post_idx) {
165 if it.is_code() {
166 post_idx = i;
167 break;
168 }
169 }
170 post_idx += 1;
171 }
172
173 let segments = &all_raws[pre_idx..post_idx];
174 ReflowSequence::from_raw_segments(segments.to_vec(), root_segment, config, None)
175 }
176
177 pub fn insert(
178 self,
179 insertion: ErasedSegment,
180 target: ErasedSegment,
181 pos: ReflowInsertPosition,
182 ) -> Self {
183 let target_idx = self.find_element_idx_with(&target);
184
185 let new_block = ReflowBlock::from_config(
186 insertion.clone(),
187 self.reflow_config,
188 self.depth_map.get_depth_info(&target),
189 );
190
191 if pos == ReflowInsertPosition::Before {
192 let mut new_elements = self.elements[..target_idx].to_vec();
193 new_elements.push(new_block.into());
194 new_elements.push(ReflowPoint::default().into());
195 new_elements.extend_from_slice(&self.elements[target_idx..]);
196
197 let new_lint_result = LintResult::new(
198 target.clone().into(),
199 vec![LintFix::create_before(target, vec![insertion])],
200 None,
201 None,
202 );
203
204 return ReflowSequence {
205 root_segment: self.root_segment,
206 elements: new_elements,
207 lint_results: vec![new_lint_result],
208 reflow_config: self.reflow_config,
209 depth_map: self.depth_map,
210 };
211 }
212
213 self
214 }
215
216 fn find_element_idx_with(&self, target: &ErasedSegment) -> usize {
217 self.elements
218 .iter()
219 .position(|elem| elem.segments().contains(target))
220 .unwrap_or_else(|| panic!("Target [{target:?}] not found in ReflowSequence."))
221 }
222
223 pub fn without(self, target: &ErasedSegment) -> ReflowSequence<'a> {
224 let removal_idx = self.find_element_idx_with(target);
225 if removal_idx == 0 || removal_idx == self.elements.len() - 1 {
226 panic!("Unexpected removal at one end of a ReflowSequence.");
227 }
228 if let ReflowElement::Point(_) = &self.elements[removal_idx] {
229 panic!("Not expected removal of whitespace in ReflowSequence.");
230 }
231 let merged_point = ReflowPoint::new(
232 [
233 self.elements[removal_idx - 1].segments(),
234 self.elements[removal_idx + 1].segments(),
235 ]
236 .concat(),
237 );
238 let mut new_elements = self.elements[..removal_idx - 1].to_vec();
239 new_elements.push(ReflowElement::Point(merged_point));
240 new_elements.extend_from_slice(&self.elements[removal_idx + 2..]);
241
242 ReflowSequence {
243 elements: new_elements,
244 root_segment: self.root_segment.clone(),
245 lint_results: vec![LintResult::new(
246 target.clone().into(),
247 vec![LintFix::delete(target.clone())],
248 None,
249 None,
250 )],
251 reflow_config: self.reflow_config,
252 depth_map: self.depth_map,
253 }
254 }
255
256 pub fn respace(mut self, tables: &Tables, strip_newlines: bool, filter: Filter) -> Self {
257 let mut lint_results = take(&mut self.lint_results);
258 let mut new_elements = Vec::new();
259
260 for (point, pre, post) in self.iter_points_with_constraints() {
261 let lint_results_len = lint_results.len();
262 let (mut new_lint_results, mut new_point) = point.respace_point(
263 tables,
264 pre,
265 post,
266 &self.root_segment,
267 lint_results,
268 strip_newlines,
269 "before",
270 );
271
272 let ignore = if new_point
273 .segments()
274 .iter()
275 .any(|seg| seg.is_type(SyntaxKind::Newline))
276 || post
277 .as_ref()
278 .is_some_and(|p| p.class_types().contains(SyntaxKind::EndOfFile))
279 {
280 filter == Filter::Inline
281 } else {
282 filter == Filter::Newline
283 };
284
285 if ignore {
286 new_point = point.clone();
287 new_lint_results.truncate(lint_results_len);
288 }
289
290 lint_results = new_lint_results;
291
292 if let Some(pre_value) = pre
293 && (new_elements.is_empty() || new_elements.last().unwrap() != pre_value)
294 {
295 new_elements.push(pre_value.clone().into());
296 }
297
298 new_elements.push(new_point.into());
299
300 if let Some(post) = post {
301 new_elements.push(post.clone().into());
302 }
303 }
304
305 self.elements = new_elements;
306 self.lint_results = lint_results;
307
308 self
309 }
310
311 pub fn rebreak(self, tables: &Tables) -> Self {
312 if !self.lint_results.is_empty() {
313 panic!("rebreak cannot currently handle pre-existing embodied fixes");
314 }
315
316 let (elem_buff, lint_results) = rebreak_sequence(tables, self.elements, &self.root_segment);
318
319 ReflowSequence {
320 root_segment: self.root_segment,
321 elements: elem_buff,
322 lint_results,
323 reflow_config: self.reflow_config,
324 depth_map: self.depth_map,
325 }
326 }
327
328 pub fn replace(mut self, target: ErasedSegment, edit: &[ErasedSegment]) -> Self {
330 let target_raws = target.get_raw_segments();
331
332 let mut edit_raws: Vec<ErasedSegment> = Vec::new();
333
334 for seg in edit {
335 edit_raws.extend_from_slice(&seg.get_raw_segments());
336 }
337
338 let trim_amount = target.path_to(&target_raws[0]).len();
339
340 for edit_raw in &edit_raws {
341 self.depth_map.copy_depth_info(
342 &target_raws[0],
343 edit_raw,
344 trim_amount.try_into().unwrap(),
345 );
346 }
347
348 let current_raws: Vec<ErasedSegment> = self
349 .elements
350 .iter()
351 .flat_map(|elem| elem.segments().iter().cloned())
352 .collect();
353
354 let start_idx = current_raws
355 .iter()
356 .position(|s| *s == target_raws[0])
357 .unwrap();
358 let last_idx = current_raws
359 .iter()
360 .position(|s| *s == *target_raws.last().unwrap())
361 .unwrap();
362
363 let new_elements = Self::elements_from_raw_segments(
364 current_raws[..start_idx]
365 .iter()
366 .chain(edit_raws.iter())
367 .chain(current_raws[last_idx + 1..].iter())
368 .cloned()
369 .collect(),
370 &self.depth_map,
371 self.reflow_config,
372 );
373
374 ReflowSequence {
375 elements: new_elements,
376 root_segment: self.root_segment,
377 reflow_config: self.reflow_config,
378 depth_map: self.depth_map,
379 lint_results: vec![LintResult::new(
380 target.clone().into(),
381 vec![LintFix::replace(target.clone(), edit.to_vec(), None)],
382 None,
383 None,
384 )],
385 }
386 }
387
388 pub fn reindent(self, tables: &Tables) -> Self {
389 if !self.lint_results.is_empty() {
390 panic!("reindent cannot currently handle pre-existing embodied fixes");
391 }
392
393 let single_indent = construct_single_indent(self.reflow_config.indent_unit);
394
395 let (elements, indent_results) = lint_indent_points(
396 tables,
397 self.elements,
398 &single_indent,
399 <_>::default(),
400 self.reflow_config.allow_implicit_indents,
401 );
402
403 Self {
404 root_segment: self.root_segment,
405 elements,
406 lint_results: indent_results,
407 reflow_config: self.reflow_config,
408 depth_map: self.depth_map,
409 }
410 }
411
412 pub fn break_long_lines(self, tables: &Tables) -> Self {
413 if !self.lint_results.is_empty() {
414 panic!("break_long_lines cannot currently handle pre-existing embodied fixes");
415 }
416
417 let single_indent = construct_single_indent(self.reflow_config.indent_unit);
418
419 let (elements, length_results) = lint_line_length(
420 tables,
421 &self.elements,
422 &self.root_segment,
423 &single_indent,
424 self.reflow_config.max_line_length,
425 self.reflow_config.allow_implicit_indents,
426 self.reflow_config.trailing_comments,
427 );
428
429 ReflowSequence {
430 root_segment: self.root_segment,
431 elements,
432 lint_results: length_results,
433 reflow_config: self.reflow_config,
434 depth_map: self.depth_map,
435 }
436 }
437
438 fn iter_points_with_constraints(
439 &self,
440 ) -> impl Iterator<Item = (&ReflowPoint, Option<&ReflowBlock>, Option<&ReflowBlock>)> + '_ {
441 self.elements.iter().enumerate().filter_map(|(idx, elem)| {
442 let point = elem.as_point()?;
443 let mut pre = None;
444 let mut post = None;
445
446 if idx > 0 {
447 pre = Some(self.elements[idx - 1].as_block().unwrap());
448 }
449
450 if idx < self.elements.len() - 1 {
451 post = Some(self.elements[idx + 1].as_block().unwrap());
452 }
453
454 (point, pre, post).into()
455 })
456 }
457
458 pub fn elements(&self) -> &[ReflowElement] {
459 &self.elements
460 }
461}
462
463#[derive(Clone, Copy, PartialEq, Eq)]
464pub enum Filter {
465 All,
466 Inline,
467 Newline,
468}