Skip to content

Commit d04c112

Browse files
committed
Prepare supporting source map
1 parent 81b2389 commit d04c112

File tree

12 files changed

+372
-40
lines changed

12 files changed

+372
-40
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ description = "manipulate string like wizards"
1111
glob = "0.3.1"
1212
index_vec = { version = "0.1.3" }
1313
rustc-hash = { version = "1.1.0" }
14-
14+
vlq = "0.5.1"
15+
serde = {version = "1.0", features = ["derive"]}
16+
serde_json = "1.0"
1517

1618
[dev-dependencies]
1719
glob = "0.3.1"
1820
criterion = { version = "0.4" }
21+
insta = "1.31.0"
1922

2023
[[bench]]
2124
name = "joiner_join"

examples/source_map.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use string_wizard::{MagicString, SourceMapOptions, UpdateOptions};
2+
3+
fn main() {
4+
let demo = "<div>
5+
hello, world
6+
</div>";
7+
let mut s = MagicString::new(demo);
8+
9+
// s.prepend("import React from 'react';\n")
10+
let update_options = UpdateOptions {
11+
store_name: true,
12+
..Default::default()
13+
};
14+
s
15+
.update_with(
16+
1,
17+
2,
18+
"v",
19+
update_options.clone(),
20+
)
21+
.update_with(
22+
3,
23+
4,
24+
"d",
25+
update_options.clone(),
26+
)
27+
.update_with(
28+
demo.len() - 4,
29+
demo.len() - 1,
30+
"h1",
31+
UpdateOptions {
32+
store_name: true,
33+
..Default::default()
34+
},
35+
);
36+
37+
let sm = s.source_map(SourceMapOptions {
38+
include_content: true,
39+
});
40+
41+
std::fs::write("./demo.jsx.map", sm.to_string()).unwrap();
42+
std::fs::write("./demo.jsx", s.to_string()).unwrap();
43+
44+
println!("{:#?}", s.to_string());
45+
}

src/chunk.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ use crate::{span::Span, ChunkIdx, CowStr, TextSize};
66
pub struct Chunk<'str> {
77
pub intro: VecDeque<CowStr<'str>>,
88
pub outro: VecDeque<CowStr<'str>>,
9-
span: Span,
10-
content: Option<CowStr<'str>>,
9+
pub span: Span,
10+
pub edited_content: Option<CowStr<'str>>,
1111
pub(crate) next: Option<ChunkIdx>,
12+
pub store_name: bool,
1213
}
1314

1415
impl<'s> Chunk<'s> {
1516
pub fn new(span: Span) -> Self {
17+
debug_assert!(span.0 < span.1);
1618
Self {
1719
span,
1820
..Default::default()
@@ -50,11 +52,13 @@ impl<'str> Chunk<'str> {
5052
}
5153

5254
pub fn split<'a>(&'a mut self, text_index: TextSize) -> Chunk<'str> {
55+
debug_assert!(text_index > self.start());
56+
debug_assert!(text_index < self.end());
5357
let first_slice_span = Span(self.start(), text_index);
5458
let last_slice_span = Span(text_index, self.end());
5559
let mut new_chunk = Chunk::new(last_slice_span);
5660
if self.is_edited() {
57-
new_chunk.edit("".into())
61+
new_chunk.edit("".into(), true, false);
5862
}
5963
std::mem::swap(&mut new_chunk.outro, &mut self.outro);
6064
self.span = first_slice_span;
@@ -68,18 +72,23 @@ impl<'str> Chunk<'str> {
6872
) -> impl Iterator<Item = &'str str> {
6973
let intro_iter = self.intro.iter().map(|frag| frag.as_ref());
7074
let source_frag = self
71-
.content
75+
.edited_content
7276
.as_deref()
7377
.unwrap_or_else(|| self.span.text(original_source.as_ref()));
7478
let outro_iter = self.outro.iter().map(|frag| frag.as_ref());
7579
intro_iter.chain(Some(source_frag)).chain(outro_iter)
7680
}
7781

78-
pub fn edit(&mut self, content: CowStr<'str>) {
79-
self.content = Some(content);
82+
pub fn edit(&mut self, content: CowStr<'str>, overwrite: bool, store_name: bool) {
83+
if overwrite {
84+
self.intro.clear();
85+
self.outro.clear();
86+
}
87+
self.store_name = store_name;
88+
self.edited_content = Some(content);
8089
}
8190

8291
pub fn is_edited(&self) -> bool {
83-
self.content.is_some()
92+
self.edited_content.is_some()
8493
}
8594
}

src/decoded_map.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use crate::{
2+
mappings::{Mappings, Segment},
3+
source_map::SourceMap,
4+
};
5+
6+
#[derive(Debug)]
7+
pub struct DecodedMap {
8+
pub version: u8,
9+
pub sources: Vec<String>,
10+
pub sources_content: Vec<String>,
11+
pub mappings: Mappings,
12+
pub names: Vec<String>,
13+
}
14+
15+
impl DecodedMap {
16+
pub fn into_source_map(self) -> SourceMap {
17+
SourceMap {
18+
version: self.version,
19+
sources: self.sources,
20+
sources_content: self.sources_content,
21+
mappings: self.mappings.encoded(),
22+
names: self.names,
23+
}
24+
}
25+
}

src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
21
mod chunk;
3-
mod span;
42
mod joiner;
3+
mod locator;
54
mod magic_string;
65
mod mappings;
7-
mod locator;
6+
mod source_map;
7+
mod span;
8+
mod decoded_map;
89

910
use std::borrow::Cow;
1011

@@ -13,8 +14,12 @@ type CowStr<'s> = Cow<'s, str>;
1314
use chunk::Chunk;
1415
use index_vec::IndexVec;
1516

16-
pub use crate::{magic_string::{MagicString, MagicStringOptions}, joiner::{Joiner, JoinerOptions}};
17-
17+
pub use crate::{
18+
joiner::{Joiner, JoinerOptions},
19+
magic_string::{
20+
mutation::UpdateOptions, source_map::SourceMapOptions, MagicString, MagicStringOptions,
21+
},
22+
};
1823

1924
index_vec::define_index_type! {
2025
struct ChunkIdx = u32;
@@ -26,4 +31,4 @@ index_vec::define_index_type! {
2631
struct SourceIdx = u32;
2732
}
2833

29-
pub(crate) type TextSize = usize;
34+
pub(crate) type TextSize = usize;

src/locator.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,38 @@ impl Locator {
3232
}
3333
}
3434

35+
#[derive(Debug, PartialEq)]
3536
pub struct Location {
3637
pub line: usize,
3738
pub column: usize,
3839
}
40+
41+
impl Location {
42+
pub fn bump_line(&mut self) {
43+
self.line += 1;
44+
self.column = 0;
45+
}
46+
}
47+
48+
#[test]
49+
fn basic() {
50+
use std::ops::Range;
51+
let source = "string\nwizard";
52+
let locator = Locator::new(source);
53+
let line_range = |line: usize| -> Range<usize> {
54+
assert!(line < locator.line_offsets.len());
55+
if line == locator.line_offsets.len() - 1 {
56+
locator.line_offsets[line]..source.len()
57+
} else {
58+
locator.line_offsets[line]..(locator.line_offsets[line + 1] - 1)
59+
}
60+
};
61+
assert_eq!(&source[line_range(0)], "string");
62+
assert_eq!(&source[line_range(1)], "wizard");
63+
64+
assert_eq!(locator.line_offsets[0], 0);
65+
assert_eq!(locator.line_offsets[1], 7);
66+
67+
assert_eq!(locator.locate(2), Location { line: 0, column: 2 });
68+
assert_eq!(locator.locate(8), Location { line: 1, column: 1 });
69+
}

src/magic_string.rs renamed to src/magic_string/mod.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
pub mod mutation;
2+
pub mod source_map;
3+
14
use std::collections::VecDeque;
25

36
use rustc_hash::FxHashMap;
@@ -17,6 +20,7 @@ pub struct MagicString<'s> {
1720
intro: VecDeque<CowStr<'s>>,
1821
outro: VecDeque<CowStr<'s>>,
1922

23+
stored_names: Vec<Span>,
2024
source: CowStr<'s>,
2125
chunks: ChunkVec<'s>,
2226
first_chunk_idx: ChunkIdx,
@@ -48,6 +52,7 @@ impl<'s> MagicString<'s> {
4852
chunk_by_end: Default::default(),
4953
// setup options
5054
filename: options.filename,
55+
stored_names: Default::default(),
5156
};
5257

5358
magic_string.chunk_by_start.insert(0, initial_chunk_idx);
@@ -168,7 +173,7 @@ impl<'s> MagicString<'s> {
168173
let chunk = &mut self.chunks[chunk_idx];
169174
chunk.intro.clear();
170175
chunk.outro.clear();
171-
chunk.edit("".into());
176+
chunk.edit("".into(), true, false);
172177
searched = if end == chunk.end() {
173178
None
174179
} else {
@@ -179,19 +184,6 @@ impl<'s> MagicString<'s> {
179184
self
180185
}
181186

182-
pub fn generate_decoded_map(&self) {
183-
let mut mappings = Mappings::default();
184-
let locator = Locator::new(&self.source);
185-
186-
self.intro.iter().for_each(|frag| {
187-
mappings.advance(frag);
188-
});
189-
190-
self.iter_chunks().for_each(|chunk| {
191-
let loc = locator.locate(chunk.start());
192-
});
193-
}
194-
195187
// --- private
196188

197189
fn prepend_intro(&mut self, content: impl Into<CowStr<'s>>) {
@@ -239,16 +231,12 @@ impl<'s> MagicString<'s> {
239231
return;
240232
}
241233

242-
let mut target = if (self.source.len() - text_index) > text_index {
243-
self.first_chunk()
234+
let (mut target, mut target_idx, search_right) = if (self.source.len() - text_index) > text_index {
235+
(self.first_chunk(), self.first_chunk_idx, true)
244236
} else {
245-
self.last_chunk()
237+
(self.last_chunk(), self.last_chunk_idx, false)
246238
};
247239

248-
let mut target_idx = self.first_chunk_idx;
249-
250-
let search_right = text_index > target.end();
251-
252240
while !target.contains(text_index) {
253241
let next_idx = if search_right {
254242
self.chunk_by_start.get(&target.end()).unwrap()
@@ -260,7 +248,6 @@ impl<'s> MagicString<'s> {
260248
}
261249

262250
let chunk_contains_index = &mut self.chunks[target_idx];
263-
264251
let new_chunk = chunk_contains_index.split(text_index);
265252
self.chunk_by_end.insert(text_index, target_idx);
266253
let new_chunk_end = new_chunk.end();

src/magic_string/mutation.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use crate::{CowStr, MagicString, TextSize};
2+
3+
#[derive(Debug, Default, Clone)]
4+
pub struct UpdateOptions {
5+
pub store_name: bool,
6+
pub overwrite: bool,
7+
}
8+
9+
impl<'text> MagicString<'text> {
10+
pub fn update(&mut self, start: TextSize, end: TextSize, content: impl Into<CowStr<'text>>) -> &mut Self {
11+
self.update_with(start, end, content, Default::default())
12+
}
13+
14+
pub fn update_with(
15+
&mut self,
16+
start: TextSize,
17+
end: TextSize,
18+
content: impl Into<CowStr<'text>>,
19+
opts: UpdateOptions,
20+
) -> &mut Self {
21+
assert!(start < end);
22+
self.split_at(start);
23+
self.split_at(end);
24+
25+
let first = self.chunk_by_start.get(&start).unwrap();
26+
let end = self.chunk_by_end.get(&start).unwrap();
27+
28+
// let chunk_after_next =
29+
30+
self.chunks[*first].edit(content.into(), opts.overwrite, opts.store_name);
31+
32+
self
33+
}
34+
}

0 commit comments

Comments
 (0)