rw_deno_core/
source_map.rs1use crate::resolve_url;
6pub use sourcemap::SourceMap;
7use std::borrow::Cow;
8use std::collections::HashMap;
9use std::rc::Rc;
10use std::str;
11
12pub trait SourceMapGetter {
13 fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>>;
15 fn get_source_line(
16 &self,
17 file_name: &str,
18 line_number: usize,
19 ) -> Option<String>;
20}
21
22impl<T> SourceMapGetter for Rc<T>
23where
24 T: SourceMapGetter + ?Sized,
25{
26 fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
27 (**self).get_source_map(file_name)
28 }
29
30 fn get_source_line(
31 &self,
32 file_name: &str,
33 line_number: usize,
34 ) -> Option<String> {
35 (**self).get_source_line(file_name, line_number)
36 }
37}
38
39pub enum SourceMapApplication {
40 Unchanged,
42 LineAndColumn {
44 line_number: u32,
45 column_number: u32,
46 },
47 LineAndColumnAndFileName {
49 file_name: String,
50 line_number: u32,
51 column_number: u32,
52 },
53}
54
55pub type SourceMapData = Cow<'static, [u8]>;
56
57pub struct SourceMapper<G: SourceMapGetter> {
58 maps: HashMap<String, Option<SourceMap>>,
59 source_lines: HashMap<(String, i64), Option<String>>,
60 getter: Option<G>,
61 pub(crate) ext_source_maps: HashMap<String, SourceMapData>,
62 pub(crate) stashed_file_name: Option<String>,
65}
66
67impl<G: SourceMapGetter> SourceMapper<G> {
68 pub fn new(getter: Option<G>) -> Self {
69 Self {
70 maps: Default::default(),
71 source_lines: Default::default(),
72 ext_source_maps: Default::default(),
73 getter,
74 stashed_file_name: Default::default(),
75 }
76 }
77
78 pub fn has_user_sources(&self) -> bool {
79 self.getter.is_some()
80 }
81
82 pub fn apply_source_map(
88 &mut self,
89 file_name: &str,
90 line_number: u32,
91 column_number: u32,
92 ) -> SourceMapApplication {
93 let line_number = line_number - 1;
95 let column_number = column_number - 1;
96
97 let getter = self.getter.as_ref();
98 let maybe_source_map =
99 self.maps.entry(file_name.to_owned()).or_insert_with(|| {
100 None
101 .or_else(|| {
102 SourceMap::from_slice(self.ext_source_maps.get(file_name)?).ok()
103 })
104 .or_else(|| {
105 SourceMap::from_slice(&getter?.get_source_map(file_name)?).ok()
106 })
107 });
108
109 let Some(source_map) = maybe_source_map.as_ref() else {
110 return SourceMapApplication::Unchanged;
111 };
112
113 let Some(token) = source_map.lookup_token(line_number, column_number)
114 else {
115 return SourceMapApplication::Unchanged;
116 };
117
118 let new_line_number = token.get_src_line() + 1;
119 let new_column_number = token.get_src_col() + 1;
120
121 let new_file_name = match token.get_source() {
122 Some(source_file_name) => {
123 if source_file_name == file_name {
124 None
125 } else {
126 match resolve_url(source_file_name) {
131 Ok(m) if m.scheme() == "blob" => None,
132 Ok(m) => Some(m.to_string()),
133 Err(_) => None,
134 }
135 }
136 }
137 None => None,
138 };
139
140 match new_file_name {
141 None => SourceMapApplication::LineAndColumn {
142 line_number: new_line_number,
143 column_number: new_column_number,
144 },
145 Some(file_name) => SourceMapApplication::LineAndColumnAndFileName {
146 file_name,
147 line_number: new_line_number,
148 column_number: new_column_number,
149 },
150 }
151 }
152
153 const MAX_SOURCE_LINE_LENGTH: usize = 150;
154
155 pub fn get_source_line(
156 &mut self,
157 file_name: &str,
158 line_number: i64,
159 ) -> Option<String> {
160 let getter = self.getter.as_ref()?;
161 self
162 .source_lines
163 .entry((file_name.to_string(), line_number))
164 .or_insert_with(|| {
165 let s = getter.get_source_line(file_name, (line_number - 1) as usize);
167 s.filter(|s| s.len() <= Self::MAX_SOURCE_LINE_LENGTH)
168 })
169 .clone()
170 }
171}