1
+ use std:: collections:: VecDeque ;
2
+
1
3
use rustc_hash:: FxHashMap ;
2
4
3
- use crate :: { chunk:: Chunk , span:: Span , ChunkIdx , ChunkVec , CowStr } ;
5
+ use crate :: { chunk:: Chunk , span:: Span , ChunkIdx , ChunkVec , CowStr , TextSize } ;
4
6
5
7
#[ derive( Debug , Default ) ]
6
8
pub struct MagicStringOptions {
@@ -9,6 +11,8 @@ pub struct MagicStringOptions {
9
11
10
12
pub struct MagicString < ' s > {
11
13
pub filename : Option < String > ,
14
+ intro : VecDeque < CowStr < ' s > > ,
15
+ outro : VecDeque < CowStr < ' s > > ,
12
16
13
17
source : CowStr < ' s > ,
14
18
source_len : u32 ,
@@ -33,6 +37,8 @@ impl<'s> MagicString<'s> {
33
37
let mut chunks = ChunkVec :: with_capacity ( 1 ) ;
34
38
let initial_chunk_idx = chunks. push ( initial_chunk) ;
35
39
let mut magic_string = Self {
40
+ intro : Default :: default ( ) ,
41
+ outro : Default :: default ( ) ,
36
42
source,
37
43
source_len,
38
44
first_chunk_idx : initial_chunk_idx,
@@ -53,7 +59,7 @@ impl<'s> MagicString<'s> {
53
59
}
54
60
55
61
pub fn append ( & mut self , source : impl Into < CowStr < ' s > > ) -> & mut Self {
56
- self . last_chunk_mut ( ) . append_outro ( source. into ( ) ) ;
62
+ self . append_outro ( source. into ( ) ) ;
57
63
self
58
64
}
59
65
@@ -65,11 +71,13 @@ impl<'s> MagicString<'s> {
65
71
/// s.append_left(2, "b");
66
72
/// assert_eq!(s.to_string(), "01ab234")
67
73
///```
68
- pub fn append_left ( & mut self , text_index : u32 , source : impl Into < CowStr < ' s > > ) -> & mut Self {
69
- self . split_at ( text_index) ;
70
- let idx = self . chunk_by_end . get ( & text_index) . unwrap ( ) ;
71
- let chunk = & mut self . chunks [ * idx] ;
72
- chunk. append_outro ( source. into ( ) ) ;
74
+ pub fn append_left ( & mut self , text_index : u32 , content : impl Into < CowStr < ' s > > ) -> & mut Self {
75
+ match self . by_end_mut ( text_index) {
76
+ Some ( chunk) => {
77
+ chunk. append_outro ( content. into ( ) ) ;
78
+ }
79
+ None => self . append_intro ( content. into ( ) ) ,
80
+ }
73
81
self
74
82
}
75
83
@@ -83,32 +91,44 @@ impl<'s> MagicString<'s> {
83
91
/// s.append_left(2, "b");
84
92
/// assert_eq!(s.to_string(), "01abAB234")
85
93
///```
86
- pub fn append_right ( & mut self , text_index : u32 , source : impl Into < CowStr < ' s > > ) -> & mut Self {
87
- self . split_at ( text_index) ;
88
- let idx = self . chunk_by_start . get ( & text_index) . unwrap ( ) ;
89
- let chunk = & mut self . chunks [ * idx] ;
90
- chunk. append_intro ( source. into ( ) ) ;
94
+ pub fn append_right ( & mut self , text_index : u32 , content : impl Into < CowStr < ' s > > ) -> & mut Self {
95
+ match self . by_start_mut ( text_index) {
96
+ Some ( chunk) => {
97
+ chunk. append_intro ( content. into ( ) ) ;
98
+ }
99
+ None => self . append_outro ( content. into ( ) ) ,
100
+ }
91
101
self
92
102
}
93
103
94
104
pub fn prepend ( & mut self , source : impl Into < CowStr < ' s > > ) -> & mut Self {
95
- self . first_chunk_mut ( ) . prepend_intro ( source. into ( ) ) ;
105
+ self . prepend_intro ( source. into ( ) ) ;
96
106
self
97
107
}
98
108
99
- pub fn prepend_left ( & mut self , text_index : u32 , content : impl Into < CowStr < ' s > > ) -> & mut Self {
100
- self . split_at ( text_index) ;
101
- let idx = self . chunk_by_end . get ( & text_index) . unwrap ( ) ;
102
- let chunk = & mut self . chunks [ * idx] ;
103
- chunk. prepend_outro ( content. into ( ) ) ;
109
+ pub fn prepend_left (
110
+ & mut self ,
111
+ text_index : TextSize ,
112
+ content : impl Into < CowStr < ' s > > ,
113
+ ) -> & mut Self {
114
+ match self . by_end_mut ( text_index) {
115
+ Some ( chunk) => chunk. prepend_outro ( content. into ( ) ) ,
116
+ None => self . prepend_intro ( content. into ( ) ) ,
117
+ }
104
118
self
105
119
}
106
120
107
- pub fn prepend_right ( & mut self , text_index : u32 , content : impl Into < CowStr < ' s > > ) -> & mut Self {
108
- self . split_at ( text_index) ;
109
- let idx = self . chunk_by_start . get ( & text_index) . unwrap ( ) ;
110
- let chunk = & mut self . chunks [ * idx] ;
111
- chunk. prepend_intro ( content. into ( ) ) ;
121
+ pub fn prepend_right (
122
+ & mut self ,
123
+ text_index : TextSize ,
124
+ content : impl Into < CowStr < ' s > > ,
125
+ ) -> & mut Self {
126
+ match self . by_start_mut ( text_index) {
127
+ Some ( chunk) => {
128
+ chunk. prepend_intro ( content. into ( ) ) ;
129
+ }
130
+ None => self . prepend_outro ( content. into ( ) ) ,
131
+ }
112
132
self
113
133
}
114
134
@@ -123,8 +143,52 @@ impl<'s> MagicString<'s> {
123
143
ret
124
144
}
125
145
146
+ pub fn remove ( & mut self , start : u32 , end : u32 ) -> & mut Self {
147
+ if start == end {
148
+ return self ;
149
+ }
150
+
151
+ assert ! ( end <= self . source_len, "end is out of bounds" ) ;
152
+ assert ! ( start < end, "end must be greater than start" ) ;
153
+
154
+ self . split_at ( start) ;
155
+ self . split_at ( end) ;
156
+
157
+ let mut searched = self . chunk_by_start . get ( & start) . copied ( ) ;
158
+
159
+ while let Some ( chunk_idx) = searched {
160
+ let chunk = & mut self . chunks [ chunk_idx] ;
161
+ chunk. intro . clear ( ) ;
162
+ chunk. outro . clear ( ) ;
163
+ chunk. edit ( "" . into ( ) ) ;
164
+ searched = if end == chunk. end ( ) {
165
+ None
166
+ } else {
167
+ self . chunk_by_start . get ( & chunk. end ( ) ) . copied ( )
168
+ }
169
+ }
170
+
171
+ self
172
+ }
173
+
126
174
// --- private
127
175
176
+ fn prepend_intro ( & mut self , content : impl Into < CowStr < ' s > > ) {
177
+ self . intro . push_front ( content. into ( ) ) ;
178
+ }
179
+
180
+ fn append_outro ( & mut self , content : impl Into < CowStr < ' s > > ) {
181
+ self . outro . push_back ( content. into ( ) ) ;
182
+ }
183
+
184
+ fn prepend_outro ( & mut self , content : impl Into < CowStr < ' s > > ) {
185
+ self . outro . push_front ( content. into ( ) ) ;
186
+ }
187
+
188
+ fn append_intro ( & mut self , content : impl Into < CowStr < ' s > > ) {
189
+ self . intro . push_back ( content. into ( ) ) ;
190
+ }
191
+
128
192
fn iter_chunks ( & self ) -> impl Iterator < Item = & Chunk > {
129
193
ChunkIter {
130
194
next : Some ( self . first_chunk_idx ) ,
@@ -133,7 +197,10 @@ impl<'s> MagicString<'s> {
133
197
}
134
198
135
199
pub ( crate ) fn fragments ( & ' s self ) -> impl Iterator < Item = & ' s str > {
136
- self . iter_chunks ( ) . flat_map ( |c| c. fragments ( & self . source ) )
200
+ let intro = self . intro . iter ( ) . map ( |s| s. as_ref ( ) ) ;
201
+ let outro = self . outro . iter ( ) . map ( |s| s. as_ref ( ) ) ;
202
+ let chunks = self . iter_chunks ( ) . flat_map ( |c| c. fragments ( & self . source ) ) ;
203
+ intro. chain ( chunks) . chain ( outro)
137
204
}
138
205
139
206
/// For input
@@ -146,16 +213,17 @@ impl<'s> MagicString<'s> {
146
213
///
147
214
/// Chunk{span: (0, 3)} => "abc"
148
215
/// Chunk{span: (3, 7)} => "defg"
149
- fn split_at ( & mut self , text_index : u32 ) {
150
- debug_assert ! ( text_index <= self . source_len) ;
151
-
152
- if self . chunk_by_start . contains_key ( & text_index)
153
- || self . chunk_by_end . contains_key ( & text_index)
154
- {
216
+ fn split_at ( & mut self , text_index : TextSize ) {
217
+ if text_index == 0 || self . chunk_by_end . contains_key ( & text_index) {
155
218
return ;
156
219
}
157
220
158
- let mut target = & self . chunks [ self . first_chunk_idx ] ;
221
+ let mut target = if ( self . source_len - text_index) > text_index {
222
+ self . first_chunk ( )
223
+ } else {
224
+ self . last_chunk ( )
225
+ } ;
226
+
159
227
let mut target_idx = self . first_chunk_idx ;
160
228
161
229
let search_right = text_index > target. end ( ) ;
@@ -186,12 +254,34 @@ impl<'s> MagicString<'s> {
186
254
chunk_contains_index. next = Some ( new_chunk_idx) ;
187
255
}
188
256
189
- fn last_chunk_mut ( & mut self ) -> & mut Chunk < ' s > {
190
- & mut self . chunks [ self . last_chunk_idx ]
257
+ fn by_start_mut ( & mut self , text_index : TextSize ) -> Option < & mut Chunk < ' s > > {
258
+ if text_index == self . source_len {
259
+ None
260
+ } else {
261
+ self . split_at ( text_index) ;
262
+ // TODO: safety: using `unwrap_unchecked` is fine.
263
+ let idx = self . chunk_by_start . get ( & text_index) . unwrap ( ) ;
264
+ Some ( & mut self . chunks [ * idx] )
265
+ }
266
+ }
267
+
268
+ fn by_end_mut ( & mut self , text_index : TextSize ) -> Option < & mut Chunk < ' s > > {
269
+ if text_index == 0 {
270
+ None
271
+ } else {
272
+ self . split_at ( text_index) ;
273
+ // TODO: safety: using `unwrap_unchecked` is fine.
274
+ let idx = self . chunk_by_end . get ( & text_index) . unwrap ( ) ;
275
+ Some ( & mut self . chunks [ * idx] )
276
+ }
277
+ }
278
+
279
+ fn last_chunk ( & self ) -> & Chunk < ' s > {
280
+ & self . chunks [ self . last_chunk_idx ]
191
281
}
192
282
193
- fn first_chunk_mut ( & mut self ) -> & mut Chunk < ' s > {
194
- & mut self . chunks [ self . first_chunk_idx ]
283
+ fn first_chunk ( & self ) -> & Chunk < ' s > {
284
+ & self . chunks [ self . first_chunk_idx ]
195
285
}
196
286
}
197
287
0 commit comments