1use std::{fmt, hash::Hash};
5
6use stdx::{always, itertools::Itertools};
7
8use crate::{
9 EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SpanData,
10 SyntaxContext, TextRange, TextSize,
11};
12
13#[derive(Debug, PartialEq, Eq, Clone, Hash)]
15pub struct SpanMap<S> {
16 spans: Vec<(TextSize, SpanData<S>)>,
18 pub matched_arm: Option<u32>,
21}
22
23impl<S> SpanMap<S>
24where
25 SpanData<S>: Copy,
26{
27 pub fn empty() -> Self {
29 Self { spans: Vec::new(), matched_arm: None }
30 }
31
32 pub fn finish(&mut self) {
35 always!(
36 self.spans.iter().tuple_windows().all(|(a, b)| a.0 < b.0),
37 "spans are not in order"
38 );
39 self.spans.shrink_to_fit();
40 }
41
42 pub fn push(&mut self, offset: TextSize, span: SpanData<S>) {
44 if cfg!(debug_assertions)
45 && let Some(&(last_offset, _)) = self.spans.last()
46 {
47 assert!(
48 last_offset < offset,
49 "last_offset({last_offset:?}) must be smaller than offset({offset:?})"
50 );
51 }
52 self.spans.push((offset, span));
53 }
54
55 pub fn ranges_with_span_exact(
59 &self,
60 span: SpanData<S>,
61 ) -> impl Iterator<Item = (TextRange, S)> + '_
62 where
63 S: Copy,
64 {
65 self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
66 if !s.eq_ignoring_ctx(span) {
67 return None;
68 }
69 let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
70 Some((TextRange::new(start, end), s.ctx))
71 })
72 }
73
74 pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = (TextRange, S)> + '_
78 where
79 S: Copy,
80 {
81 self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
82 if s.anchor != span.anchor {
83 return None;
84 }
85 if !s.range.contains_range(span.range) {
86 return None;
87 }
88 let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
89 Some((TextRange::new(start, end), s.ctx))
90 })
91 }
92
93 pub fn span_at(&self, offset: TextSize) -> SpanData<S> {
95 let entry = self.spans.partition_point(|&(it, _)| it <= offset);
96 self.spans[entry].1
97 }
98
99 pub fn spans_for_range(&self, range: TextRange) -> impl Iterator<Item = SpanData<S>> + '_ {
102 let (start, end) = (range.start(), range.end());
103 let start_entry = self.spans.partition_point(|&(it, _)| it <= start);
104 let end_entry = self.spans[start_entry..].partition_point(|&(it, _)| it <= end); self.spans[start_entry..][..end_entry].iter().map(|&(_, s)| s)
106 }
107
108 pub fn iter(&self) -> impl Iterator<Item = (TextSize, SpanData<S>)> + '_ {
109 self.spans.iter().copied()
110 }
111
112 pub fn merge(&mut self, other_range: TextRange, other_size: TextSize, other: &SpanMap<S>) {
116 self.spans.retain_mut(|(offset, _)| {
138 if other_range.start() < *offset && *offset <= other_range.end() {
139 false
140 } else {
141 if *offset > other_range.end() {
142 *offset += other_size;
143 *offset -= other_range.len();
144 }
145 true
146 }
147 });
148
149 self.spans
150 .extend(other.spans.iter().map(|&(offset, span)| (offset + other_range.start(), span)));
151
152 self.spans.sort_unstable_by_key(|&(offset, _)| offset);
153
154 self.matched_arm = None;
156 }
157}
158
159#[derive(PartialEq, Eq, Hash, Debug)]
160pub struct RealSpanMap {
161 file_id: EditionedFileId,
162 pairs: Box<[(TextSize, ErasedFileAstId)]>,
165 end: TextSize,
166}
167
168impl fmt::Display for RealSpanMap {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 writeln!(f, "RealSpanMap({:?}):", self.file_id)?;
171 for span in self.pairs.iter() {
172 writeln!(f, "{}: {:#?}", u32::from(span.0), span.1)?;
173 }
174 Ok(())
175 }
176}
177
178impl RealSpanMap {
179 pub fn absolute(file_id: EditionedFileId) -> Self {
181 RealSpanMap {
182 file_id,
183 pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]),
184 end: TextSize::new(!0),
185 }
186 }
187
188 pub fn from_file(
189 file_id: EditionedFileId,
190 pairs: Box<[(TextSize, ErasedFileAstId)]>,
191 end: TextSize,
192 ) -> Self {
193 Self { file_id, pairs, end }
194 }
195
196 pub fn span_for_range(&self, range: TextRange) -> Span {
197 assert!(
198 range.end() <= self.end,
199 "range {range:?} goes beyond the end of the file {:?}",
200 self.end
201 );
202 let start = range.start();
203 let idx = self
204 .pairs
205 .binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less))
206 .unwrap_err();
207 let (offset, ast_id) = self.pairs[idx - 1];
208 Span {
209 range: range - offset,
210 anchor: SpanAnchor { file_id: self.file_id, ast_id },
211 ctx: SyntaxContext::root(self.file_id.edition()),
212 }
213 }
214}