Skip to content

Commit 4b7e115

Browse files
committed
refactor rope in ReplaceSource
1 parent 851fa2b commit 4b7e115

File tree

1 file changed

+86
-124
lines changed

1 file changed

+86
-124
lines changed

src/replace_source.rs

Lines changed: 86 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -170,141 +170,100 @@ impl Source for ReplaceSource {
170170
}
171171

172172
fn rope(&self) -> Vec<&str> {
173-
let inner_source_code = self.inner.rope();
174-
175-
if self.replacements.is_empty() {
176-
return inner_source_code;
177-
}
178-
179-
let mut result = Vec::new();
180-
let mut pos: u32 = 0;
181-
let mut chunk_index = 0;
182-
let mut chunk_pos = 0; // Position within current chunk
183-
let mut replacement_index = 0;
184-
185-
// Calculate total length to determine positions
186-
let mut chunk_start_positions = Vec::new();
187-
let mut total_pos = 0;
188-
for chunk in &inner_source_code {
189-
chunk_start_positions.push(total_pos);
190-
total_pos += chunk.len() as u32;
191-
}
192-
193-
while replacement_index < self.replacements.len()
194-
|| chunk_index < inner_source_code.len()
195-
{
196-
let next_replacement = self.replacements.get(replacement_index);
197-
198-
// Process chunks until we hit a replacement or finish
199-
while chunk_index < inner_source_code.len() {
200-
let chunk = inner_source_code[chunk_index];
201-
let chunk_start = chunk_start_positions[chunk_index];
202-
let chunk_end = chunk_start + chunk.len() as u32;
203-
204-
// Check if there's a replacement that starts within this chunk
205-
if let Some(replacement) = next_replacement {
206-
if replacement.start >= chunk_start && replacement.start < chunk_end {
207-
// Replacement starts within this chunk
208-
let offset_in_chunk = (replacement.start - chunk_start) as usize;
209-
210-
// Add the part of chunk before replacement
211-
if offset_in_chunk > chunk_pos {
212-
result.push(&chunk[chunk_pos..offset_in_chunk]);
213-
}
214-
215-
// Add replacement content
216-
result.push(&replacement.content);
173+
let inner_rope = self.inner.rope();
174+
let mut rope =
175+
Vec::with_capacity(inner_rope.len() + self.replacements.len() * 2);
176+
177+
let mut pos: usize = 0;
178+
let mut replacement_idx: usize = 0;
179+
let mut replacement_end: Option<usize> = None;
180+
let mut next_replacement: Option<usize> = (replacement_idx
181+
< self.replacements.len())
182+
.then(|| self.replacements[replacement_idx].start as usize);
183+
184+
'chunk_loop: for chunk in self.inner.rope() {
185+
let mut chunk_pos = 0;
186+
let end_pos = pos + chunk.len();
187+
188+
// Skip over when it has been replaced
189+
if let Some(replacement_end) =
190+
replacement_end.filter(|replacement_end| *replacement_end > pos)
191+
{
192+
// Skip over the whole chunk
193+
if replacement_end >= end_pos {
194+
pos = end_pos;
195+
continue;
196+
}
197+
// Partially skip over chunk
198+
chunk_pos = replacement_end - pos;
199+
pos += chunk_pos;
200+
}
217201

218-
// Update positions
219-
pos = replacement.end;
220-
replacement_index += 1;
202+
// Is a replacement in the chunk?
203+
while let Some(next_replacement_pos) = next_replacement
204+
.filter(|next_replacement_pos| *next_replacement_pos < end_pos)
205+
{
206+
if next_replacement_pos > pos {
207+
// Emit chunk until replacement
208+
let offset = next_replacement_pos - pos;
209+
let chunk_slice = &chunk[chunk_pos..(chunk_pos + offset)];
210+
rope.push(chunk_slice);
211+
chunk_pos += offset;
212+
pos = next_replacement_pos;
213+
}
214+
// Insert replacement content split into chunks by lines
215+
let replacement = &self.replacements[replacement_idx];
216+
rope.push(&replacement.content);
221217

222-
// Find where to continue after replacement
223-
let mut found_continue_pos = false;
224-
for (idx, &chunk_start_pos) in
225-
chunk_start_positions.iter().enumerate()
226-
{
227-
let chunk_end_pos =
228-
chunk_start_pos + inner_source_code[idx].len() as u32;
229-
230-
if pos >= chunk_start_pos && pos < chunk_end_pos {
231-
// Continue from within this chunk
232-
chunk_index = idx;
233-
chunk_pos = (pos - chunk_start_pos) as usize;
234-
found_continue_pos = true;
235-
break;
236-
} else if pos <= chunk_start_pos {
237-
// Continue from the start of this chunk
238-
chunk_index = idx;
239-
chunk_pos = 0;
240-
found_continue_pos = true;
241-
break;
242-
}
243-
}
218+
// Remove replaced content by settings this variable
219+
replacement_end = if let Some(replacement_end) = replacement_end {
220+
Some(replacement_end.max(replacement.end as usize))
221+
} else {
222+
Some(replacement.end as usize)
223+
};
244224

245-
if !found_continue_pos {
246-
// Replacement goes beyond all chunks
247-
chunk_index = inner_source_code.len();
248-
}
225+
// Move to next replacement
226+
replacement_idx += 1;
227+
next_replacement = if replacement_idx < self.replacements.len() {
228+
Some(self.replacements[replacement_idx].start as usize)
229+
} else {
230+
None
231+
};
249232

250-
break;
251-
} else if replacement.start < chunk_start {
252-
// Replacement starts before this chunk
253-
result.push(&replacement.content);
254-
replacement_index += 1;
255-
256-
// Skip chunks that are replaced
257-
pos = replacement.end;
258-
while chunk_index < inner_source_code.len() {
259-
let current_chunk_start = chunk_start_positions[chunk_index];
260-
let current_chunk_end = current_chunk_start
261-
+ inner_source_code[chunk_index].len() as u32;
262-
263-
if pos <= current_chunk_start {
264-
// Start from beginning of this chunk
265-
chunk_pos = 0;
266-
break;
267-
} else if pos < current_chunk_end {
268-
// Start from middle of this chunk
269-
chunk_pos = (pos - current_chunk_start) as usize;
270-
break;
271-
} else {
272-
// Skip this entire chunk
273-
chunk_index += 1;
274-
}
275-
}
276-
break;
233+
// Skip over when it has been replaced
234+
let offset = chunk.len() as i64 - end_pos as i64
235+
+ replacement_end.unwrap() as i64
236+
- chunk_pos as i64;
237+
if offset > 0 {
238+
// Skip over whole chunk
239+
if replacement_end
240+
.is_some_and(|replacement_end| replacement_end >= end_pos)
241+
{
242+
pos = end_pos;
243+
continue 'chunk_loop;
277244
}
278-
}
279245

280-
// No replacement affecting this chunk, add the remaining part
281-
if chunk_pos == 0
282-
&& (next_replacement.is_none()
283-
|| next_replacement.unwrap().start > chunk_end)
284-
{
285-
// Add entire chunk
286-
result.push(chunk);
287-
} else if chunk_pos < chunk.len() {
288-
// Add remaining part of chunk
289-
result.push(&chunk[chunk_pos..]);
246+
// Partially skip over chunk
247+
chunk_pos += offset as usize;
248+
pos += offset as usize;
290249
}
291-
292-
chunk_index += 1;
293-
chunk_pos = 0;
294-
pos = chunk_end;
295250
}
296251

297-
// Handle remaining replacements that are beyond all chunks
298-
while replacement_index < self.replacements.len() {
299-
let replacement = &self.replacements[replacement_index];
300-
if replacement.start >= pos {
301-
result.push(&replacement.content);
302-
}
303-
replacement_index += 1;
252+
// Emit remaining chunk
253+
if chunk_pos < chunk.len() {
254+
rope.push(&chunk[chunk_pos..]);
304255
}
256+
pos = end_pos;
305257
}
306258

307-
result
259+
// Handle remaining replacements one by one
260+
while replacement_idx < self.replacements.len() {
261+
let content = &self.replacements[replacement_idx].content;
262+
rope.push(content);
263+
replacement_idx += 1;
264+
}
265+
266+
rope
308267
}
309268

310269
fn buffer(&self) -> Cow<[u8]> {
@@ -365,7 +324,10 @@ impl Source for ReplaceSource {
365324
}
366325

367326
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
368-
writer.write_all(self.source().as_bytes())
327+
for text in self.rope() {
328+
writer.write_all(text.as_bytes())?;
329+
}
330+
Ok(())
369331
}
370332
}
371333

0 commit comments

Comments
 (0)