Skip to main content

fallow_extract/
source_map.rs

1//! Source-position mapping for code extracted from container formats.
2
3use oxc_span::Span;
4
5#[derive(Debug, Clone)]
6struct FragmentMap {
7    generated_start: u32,
8    original_start: u32,
9    len: u32,
10}
11
12/// Extracted source plus byte-offset mappings back to the original file.
13#[derive(Debug, Clone, Default)]
14pub struct ExtractionResult {
15    /// Source text passed to the JavaScript parser.
16    pub body: String,
17    fragments: Vec<FragmentMap>,
18}
19
20impl ExtractionResult {
21    /// Build a mapped result for one contiguous slice.
22    #[must_use]
23    pub fn contiguous(body: &str, original_start: usize) -> Self {
24        let mut result = Self::default();
25        result.push_mapped(body, original_start);
26        result
27    }
28
29    /// Append original source text to the extracted body.
30    pub fn push_mapped(&mut self, text: &str, original_start: usize) {
31        if text.is_empty() {
32            return;
33        }
34        let generated_start = self.body.len();
35        self.body.push_str(text);
36        self.fragments.push(FragmentMap {
37            generated_start: generated_start as u32,
38            original_start: original_start as u32,
39            len: text.len() as u32,
40        });
41    }
42
43    /// Map an extracted-buffer byte offset back to the original source.
44    #[must_use]
45    pub fn original_offset(&self, offset: u32) -> Option<u32> {
46        self.original_offset_start_biased(offset)
47    }
48
49    fn original_offset_start_biased(&self, offset: u32) -> Option<u32> {
50        let idx = self
51            .fragments
52            .partition_point(|fragment| fragment.generated_start <= offset)
53            .checked_sub(1)?;
54        let fragment = &self.fragments[idx];
55        let delta = offset.checked_sub(fragment.generated_start)?;
56        if delta <= fragment.len {
57            Some(fragment.original_start + delta)
58        } else {
59            None
60        }
61    }
62
63    fn original_offset_end_biased(&self, offset: u32) -> Option<u32> {
64        let idx = self
65            .fragments
66            .partition_point(|fragment| fragment.generated_start < offset)
67            .checked_sub(1)?;
68        let fragment = &self.fragments[idx];
69        let delta = offset.checked_sub(fragment.generated_start)?;
70        if delta <= fragment.len {
71            Some(fragment.original_start + delta)
72        } else {
73            None
74        }
75    }
76
77    /// Remap a span from extracted-buffer offsets to original-source offsets.
78    #[must_use]
79    pub fn remap_span(&self, span: Span) -> Span {
80        if span.start == 0 && span.end == 0 {
81            return span;
82        }
83        let Some(start) = self.original_offset(span.start) else {
84            return span;
85        };
86        let Some(end) = self.original_offset_end_biased(span.end) else {
87            return span;
88        };
89        Span::new(start, end)
90    }
91}