sqruff_lib_core/parser/
match_result.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3
4use ahash::HashMapExt;
5use nohash_hasher::IntMap;
6
7use super::segments::base::{ErasedSegment, SegmentBuilder, Tables};
8use crate::dialects::init::DialectKind;
9use crate::dialects::syntax::SyntaxKind;
10use crate::parser::markers::PositionMarker;
11
12fn get_point_pos_at_idx(segments: &[ErasedSegment], idx: u32) -> PositionMarker {
13    let idx = idx as usize;
14    if idx < segments.len() {
15        segments[idx]
16            .get_position_marker()
17            .unwrap()
18            .start_point_marker()
19    } else {
20        segments[idx - 1]
21            .get_position_marker()
22            .unwrap()
23            .end_point_marker()
24    }
25}
26
27#[derive(Debug, Clone)]
28pub enum Matched {
29    SyntaxKind(SyntaxKind),
30    Newtype(SyntaxKind),
31}
32
33#[derive(Default, Debug, Clone)]
34pub struct MatchResult {
35    pub span: Span,
36    pub matched: Option<Matched>,
37    pub insert_segments: Vec<(u32, SyntaxKind)>,
38    pub child_matches: Vec<MatchResult>,
39}
40
41impl MatchResult {
42    pub fn from_span(start: u32, end: u32) -> Self {
43        Self {
44            span: Span { start, end },
45            ..Default::default()
46        }
47    }
48
49    pub fn empty_at(idx: u32) -> Self {
50        Self::from_span(idx, idx)
51    }
52
53    pub fn len(&self) -> u32 {
54        self.span.end - self.span.start
55    }
56
57    pub fn is_empty(&self) -> bool {
58        !self.has_match()
59    }
60
61    #[allow(clippy::len_zero)]
62    pub fn has_match(&self) -> bool {
63        self.len() > 0 || !self.insert_segments.is_empty()
64    }
65
66    pub fn is_better_than(&self, other: &MatchResult) -> bool {
67        self.len() > other.len()
68    }
69
70    pub(crate) fn append<'a>(self, other: impl Into<Cow<'a, MatchResult>>) -> Self {
71        let other = other.into();
72        let mut insert_segments = Vec::new();
73
74        if self.is_empty() {
75            return other.into_owned();
76        }
77
78        if other.is_empty() {
79            return self;
80        }
81
82        let new_span = Span {
83            start: self.span.start,
84            end: other.span.end,
85        };
86        let mut child_matches = Vec::new();
87        for mut matched in [self, other.into_owned()] {
88            if matched.matched.is_some() {
89                child_matches.push(matched);
90            } else {
91                insert_segments.append(&mut matched.insert_segments);
92                child_matches.append(&mut matched.child_matches);
93            }
94        }
95
96        MatchResult {
97            span: new_span,
98            insert_segments,
99            child_matches,
100            ..Default::default()
101        }
102    }
103
104    pub(crate) fn wrap(self, outer_matched: Matched) -> Self {
105        if self.is_empty() {
106            return self;
107        }
108
109        let mut insert_segments = Vec::new();
110        let span = self.span;
111        let child_matches = if self.matched.is_some() {
112            vec![self]
113        } else {
114            insert_segments = self.insert_segments;
115            self.child_matches
116        };
117
118        Self {
119            span,
120            matched: Some(outer_matched),
121            insert_segments,
122            child_matches,
123        }
124    }
125
126    pub fn apply(
127        self,
128        tables: &Tables,
129        dialect: DialectKind,
130        segments: &[ErasedSegment],
131    ) -> Vec<ErasedSegment> {
132        enum Trigger {
133            MatchResult(MatchResult),
134            Meta(SyntaxKind),
135        }
136
137        let mut result_segments = Vec::new();
138        let mut trigger_locs: IntMap<u32, Vec<Trigger>> =
139            IntMap::with_capacity(self.insert_segments.len() + self.child_matches.len());
140
141        for (pos, insert) in self.insert_segments {
142            trigger_locs
143                .entry(pos)
144                .or_default()
145                .push(Trigger::Meta(insert));
146        }
147
148        for match_result in self.child_matches {
149            trigger_locs
150                .entry(match_result.span.start)
151                .or_default()
152                .push(Trigger::MatchResult(match_result));
153        }
154
155        let mut max_idx = self.span.start;
156        let mut keys = Vec::from_iter(trigger_locs.keys().copied());
157        keys.sort();
158
159        for idx in keys {
160            match idx.cmp(&max_idx) {
161                Ordering::Greater => {
162                    result_segments.extend_from_slice(&segments[max_idx as usize..idx as usize]);
163                    max_idx = idx;
164                }
165                Ordering::Less => {
166                    unreachable!("This MatchResult was wrongly constructed")
167                }
168                Ordering::Equal => {}
169            }
170
171            for trigger in trigger_locs.remove(&idx).unwrap() {
172                match trigger {
173                    Trigger::MatchResult(trigger) => {
174                        max_idx = trigger.span.end;
175                        result_segments.append(&mut trigger.apply(tables, dialect, segments));
176                    }
177                    Trigger::Meta(meta) => {
178                        let pos = get_point_pos_at_idx(segments, idx);
179                        let meta = SegmentBuilder::token(tables.next_id(), "", meta)
180                            .with_position(pos)
181                            .finish();
182                        result_segments.push(meta);
183                    }
184                }
185            }
186        }
187
188        if max_idx < self.span.end {
189            result_segments.extend_from_slice(&segments[max_idx as usize..self.span.end as usize])
190        }
191
192        let Some(matched) = self.matched else {
193            return result_segments;
194        };
195
196        match matched {
197            Matched::SyntaxKind(kind) => {
198                vec![
199                    SegmentBuilder::node(tables.next_id(), kind, dialect, result_segments)
200                        .position_from_segments()
201                        .finish(),
202                ]
203            }
204            Matched::Newtype(kind) => {
205                let old = result_segments.pop().unwrap();
206
207                vec![
208                    SegmentBuilder::token(old.id(), old.raw().as_ref(), kind)
209                        .with_position(old.get_position_marker().unwrap().clone())
210                        .finish(),
211                ]
212            }
213        }
214    }
215}
216
217impl<'a> From<&'a MatchResult> for Cow<'a, MatchResult> {
218    fn from(t: &'a MatchResult) -> Self {
219        Cow::Borrowed(t)
220    }
221}
222
223impl From<MatchResult> for Cow<'_, MatchResult> {
224    fn from(t: MatchResult) -> Self {
225        Cow::Owned(t)
226    }
227}
228
229#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
230pub struct Span {
231    pub start: u32,
232    pub end: u32,
233}