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::base::{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::base::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 [{:?}] not found in ReflowSequence.", target))
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 if new_elements.is_empty() || new_elements.last().unwrap() != pre_value {
294 new_elements.push(pre_value.clone().into());
295 }
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) =
318 rebreak_sequence(tables, self.elements, self.root_segment.clone());
319
320 ReflowSequence {
321 root_segment: self.root_segment,
322 elements: elem_buff,
323 lint_results,
324 reflow_config: self.reflow_config,
325 depth_map: self.depth_map,
326 }
327 }
328
329 pub fn replace(mut self, target: ErasedSegment, edit: &[ErasedSegment]) -> Self {
331 let target_raws = target.get_raw_segments();
332
333 let mut edit_raws: Vec<ErasedSegment> = Vec::new();
334
335 for seg in edit {
336 edit_raws.extend_from_slice(&seg.get_raw_segments());
337 }
338
339 let trim_amount = target.path_to(&target_raws[0]).len();
340
341 for edit_raw in &edit_raws {
342 self.depth_map.copy_depth_info(
343 &target_raws[0],
344 edit_raw,
345 trim_amount.try_into().unwrap(),
346 );
347 }
348
349 let current_raws: Vec<ErasedSegment> = self
350 .elements
351 .iter()
352 .flat_map(|elem| elem.segments().iter().cloned())
353 .collect();
354
355 let start_idx = current_raws
356 .iter()
357 .position(|s| *s == target_raws[0])
358 .unwrap();
359 let last_idx = current_raws
360 .iter()
361 .position(|s| *s == *target_raws.last().unwrap())
362 .unwrap();
363
364 let new_elements = Self::elements_from_raw_segments(
365 current_raws[..start_idx]
366 .iter()
367 .chain(edit_raws.iter())
368 .chain(current_raws[last_idx + 1..].iter())
369 .cloned()
370 .collect(),
371 &self.depth_map,
372 self.reflow_config,
373 );
374
375 ReflowSequence {
376 elements: new_elements,
377 root_segment: self.root_segment,
378 reflow_config: self.reflow_config,
379 depth_map: self.depth_map,
380 lint_results: vec![LintResult::new(
381 target.clone().into(),
382 vec![LintFix::replace(target.clone(), edit.to_vec(), None)],
383 None,
384 None,
385 )],
386 }
387 }
388
389 pub fn reindent(self, tables: &Tables) -> Self {
390 if !self.lint_results.is_empty() {
391 panic!("reindent cannot currently handle pre-existing embodied fixes");
392 }
393
394 let single_indent = construct_single_indent(self.reflow_config.indent_unit);
395
396 let (elements, indent_results) = lint_indent_points(
397 tables,
398 self.elements,
399 &single_indent,
400 <_>::default(),
401 self.reflow_config.allow_implicit_indents,
402 );
403
404 Self {
405 root_segment: self.root_segment,
406 elements,
407 lint_results: indent_results,
408 reflow_config: self.reflow_config,
409 depth_map: self.depth_map,
410 }
411 }
412
413 pub fn break_long_lines(self, tables: &Tables) -> Self {
414 if !self.lint_results.is_empty() {
415 panic!("break_long_lines cannot currently handle pre-existing embodied fixes");
416 }
417
418 let single_indent = construct_single_indent(self.reflow_config.indent_unit);
419
420 let (elements, length_results) = lint_line_length(
421 tables,
422 &self.elements,
423 &self.root_segment,
424 &single_indent,
425 self.reflow_config.max_line_length,
426 self.reflow_config.allow_implicit_indents,
427 self.reflow_config.trailing_comments,
428 );
429
430 ReflowSequence {
431 root_segment: self.root_segment,
432 elements,
433 lint_results: length_results,
434 reflow_config: self.reflow_config,
435 depth_map: self.depth_map,
436 }
437 }
438
439 fn iter_points_with_constraints(
440 &self,
441 ) -> impl Iterator<Item = (&ReflowPoint, Option<&ReflowBlock>, Option<&ReflowBlock>)> + '_ {
442 self.elements.iter().enumerate().filter_map(|(idx, elem)| {
443 let point = elem.as_point()?;
444 let mut pre = None;
445 let mut post = None;
446
447 if idx > 0 {
448 pre = Some(self.elements[idx - 1].as_block().unwrap());
449 }
450
451 if idx < self.elements.len() - 1 {
452 post = Some(self.elements[idx + 1].as_block().unwrap());
453 }
454
455 (point, pre, post).into()
456 })
457 }
458
459 pub fn elements(&self) -> &[ReflowElement] {
460 &self.elements
461 }
462}
463
464#[derive(Clone, Copy, PartialEq, Eq)]
465pub enum Filter {
466 All,
467 Inline,
468 Newline,
469}