sqruff_lib/utils/reflow/
depth_map.rs1use ahash::{AHashMap, AHashSet};
2use nohash_hasher::{IntMap, IntSet};
3use sqruff_lib_core::dialects::syntax::SyntaxSet;
4use sqruff_lib_core::parser::segments::{ErasedSegment, PathStep};
5
6#[derive(Debug, PartialEq, Eq, Clone)]
8pub struct StackPosition {
9 pub idx: usize,
10 pub len: usize,
11 pub type_: Option<StackPositionType>,
12}
13
14#[derive(Debug, PartialEq, Eq, Clone)]
15pub enum StackPositionType {
16 Solo,
17 Start,
18 End,
19}
20
21impl StackPosition {
22 fn stack_pos_interpreter(path_step: &PathStep) -> Option<StackPositionType> {
24 if path_step.code_idxs.is_empty() {
25 None
26 } else if path_step.code_idxs.len() == 1 {
27 Some(StackPositionType::Solo)
28 } else if path_step.idx == *path_step.code_idxs.first().unwrap() {
29 Some(StackPositionType::Start)
30 } else if path_step.idx == *path_step.code_idxs.last().unwrap() {
31 Some(StackPositionType::End)
32 } else {
33 None
34 }
35 }
36
37 fn from_path_step(path_step: &PathStep) -> StackPosition {
39 StackPosition {
40 idx: path_step.idx,
41 len: path_step.len,
42 type_: StackPosition::stack_pos_interpreter(path_step),
43 }
44 }
45}
46
47pub struct DepthMap {
48 depth_info: AHashMap<u32, DepthInfo>,
49}
50
51impl DepthMap {
52 fn new<'a>(raws_with_stack: impl Iterator<Item = &'a (ErasedSegment, Vec<PathStep>)>) -> Self {
53 let depth_info = raws_with_stack
54 .into_iter()
55 .map(|(raw, stack)| (raw.id(), DepthInfo::from_stack(stack)))
56 .collect();
57 Self { depth_info }
58 }
59
60 pub fn get_depth_info(&self, seg: &ErasedSegment) -> DepthInfo {
61 self.depth_info[&seg.id()].clone()
62 }
63
64 pub fn copy_depth_info(
65 &mut self,
66 anchor: &ErasedSegment,
67 new_segment: &ErasedSegment,
68 trim: u32,
69 ) {
70 self.depth_info.insert(
71 new_segment.id(),
72 self.get_depth_info(anchor).trim(trim.try_into().unwrap()),
73 );
74 }
75
76 pub fn from_parent(parent: &ErasedSegment) -> Self {
77 Self::new(parent.raw_segments_with_ancestors().iter())
78 }
79
80 pub fn from_raws_and_root(
81 raw_segments: impl Iterator<Item = ErasedSegment>,
82 root_segment: &ErasedSegment,
83 ) -> DepthMap {
84 let depth_info = raw_segments
85 .into_iter()
86 .map(|raw| {
87 let stack = root_segment.path_to(&raw);
88 (raw.id(), DepthInfo::from_stack(&stack))
89 })
90 .collect();
91
92 DepthMap { depth_info }
93 }
94}
95
96#[derive(Debug, PartialEq, Eq, Clone)]
98pub struct DepthInfo {
99 pub stack_depth: usize,
100 pub stack_hashes: Vec<u64>,
101 pub stack_hash_set: IntSet<u64>,
103 pub stack_class_types: Vec<SyntaxSet>,
104 pub stack_positions: IntMap<u64, StackPosition>,
105}
106
107impl DepthInfo {
108 fn from_stack(stack: &[PathStep]) -> DepthInfo {
109 let mut stack_hashes = Vec::with_capacity(stack.len());
112 let mut stack_hash_set: IntSet<u64> = IntSet::default();
113 stack_hash_set.reserve(stack.len());
114 let mut stack_class_types = Vec::with_capacity(stack.len());
115 let mut stack_positions: IntMap<u64, StackPosition> = IntMap::default();
116 stack_positions.reserve(stack.len());
117
118 for path in stack {
119 let hash = path.segment.hash_value();
120 stack_hashes.push(hash);
121 stack_hash_set.insert(hash);
122 stack_class_types.push(path.segment.class_types().clone());
123 stack_positions.insert(hash, StackPosition::from_path_step(path));
124 }
125
126 DepthInfo {
127 stack_depth: stack_hashes.len(),
128 stack_hashes,
129 stack_hash_set,
130 stack_class_types,
131 stack_positions,
132 }
133 }
134
135 pub fn trim(self, amount: usize) -> DepthInfo {
136 if amount == 0 {
138 return self;
140 }
141
142 let slice_set: IntSet<_> = IntSet::from_iter(
143 self.stack_hashes[self.stack_hashes.len() - amount..]
144 .iter()
145 .copied(),
146 );
147
148 let new_hash_set: IntSet<_> = self
149 .stack_hash_set
150 .difference(&slice_set)
151 .copied()
152 .collect();
153
154 let stack_positions = self
155 .stack_positions
156 .into_iter()
157 .filter(|(hash, _)| new_hash_set.contains(hash))
158 .collect();
159
160 DepthInfo {
161 stack_depth: self.stack_depth - amount,
162 stack_hashes: self.stack_hashes[..self.stack_hashes.len() - amount].to_vec(),
163 stack_hash_set: new_hash_set,
164 stack_class_types: self.stack_class_types[..self.stack_class_types.len() - amount]
165 .to_vec(),
166 stack_positions,
167 }
168 }
169
170 pub fn common_with(&self, other: &DepthInfo) -> Vec<u64> {
171 let common_hashes: AHashSet<_> = self
176 .stack_hash_set
177 .intersection(&other.stack_hash_set)
178 .copied()
179 .collect();
180
181 assert!(
185 !common_hashes.is_empty(),
186 "DepthInfo comparison shares no common ancestor!"
187 );
188
189 let common_depth = common_hashes.len();
190 self.stack_hashes
191 .iter()
192 .take(common_depth)
193 .copied()
194 .collect()
195 }
196}