1use crate::{
2 EditorExpectedSyntax, EditorExpectedSyntaxKind, OperationControl, OperationControlResult,
3 SourceSpan,
4};
5use std::ops::Range;
6
7const CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES: usize = 4 * 1024;
8const CONTROLLED_EDIT_REBUILD_CHECKPOINT_ITEMS: usize = 128;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub(super) enum ReplacementMapping {
12 ExactBytes,
13 Boundaries,
14}
15
16#[derive(Debug, Clone)]
17pub(super) struct SourceEdit {
18 range: Range<usize>,
19 replacement: String,
20 mapping: ReplacementMapping,
21}
22
23impl SourceEdit {
24 pub(super) fn delete(range: Range<usize>) -> Self {
25 Self {
26 range,
27 replacement: String::new(),
28 mapping: ReplacementMapping::Boundaries,
29 }
30 }
31
32 pub(super) fn replace(
33 range: Range<usize>,
34 replacement: impl Into<String>,
35 mapping: ReplacementMapping,
36 ) -> Self {
37 Self {
38 range,
39 replacement: replacement.into(),
40 mapping,
41 }
42 }
43}
44
45#[derive(Debug, Clone)]
47pub struct PreprocessedSource {
48 text: String,
49 edit_map: SourceEditMap,
50 global_expected_syntax: Vec<EditorExpectedSyntax>,
51 global_directive_prefixes: Vec<String>,
52 recovered_incomplete_directive: bool,
53 frontmatter_line_offset: usize,
54 first_line_column_offset: usize,
55}
56
57impl PreprocessedSource {
58 pub fn new(source: &str) -> Self {
59 Self::new_controlled(source, &OperationControl::new())
60 .expect("a private parse control cannot be cancelled")
61 }
62
63 pub(super) fn new_controlled(
64 source: &str,
65 control: &OperationControl,
66 ) -> OperationControlResult<Self> {
67 control.checkpoint()?;
68 let mut text = String::with_capacity(source.len());
69 let mut chunk_start = 0usize;
70 while source.len().saturating_sub(chunk_start) >= CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES {
71 let mut chunk_end = chunk_start + CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES;
72 while !source.is_char_boundary(chunk_end) {
73 chunk_end += 1;
74 }
75 text.push_str(&source[chunk_start..chunk_end]);
76 control.checkpoint()?;
77 chunk_start = chunk_end;
78 }
79 text.push_str(&source[chunk_start..]);
80 control.checkpoint()?;
81 Ok(Self {
82 text,
83 edit_map: SourceEditMap::identity(source.len()),
84 global_expected_syntax: Vec::new(),
85 global_directive_prefixes: Vec::new(),
86 recovered_incomplete_directive: false,
87 frontmatter_line_offset: 0,
88 first_line_column_offset: 0,
89 })
90 }
91
92 pub fn text(&self) -> &str {
93 &self.text
94 }
95
96 pub(super) fn copy_global_editor_evidence_from(&mut self, source: &Self) {
97 self.global_expected_syntax
98 .clone_from(&source.global_expected_syntax);
99 self.global_directive_prefixes
100 .clone_from(&source.global_directive_prefixes);
101 }
102
103 pub(crate) fn parser_position_offset(&self) -> (usize, usize) {
104 (self.frontmatter_line_offset, self.first_line_column_offset)
105 }
106
107 pub(super) fn record_frontmatter_removal(&mut self, end: usize) {
108 self.frontmatter_line_offset += self.text[..end]
109 .bytes()
110 .filter(|byte| *byte == b'\n')
111 .count();
112 self.first_line_column_offset = 0;
113 }
114
115 pub(super) fn set_first_line_column_offset(&mut self, columns: usize) {
116 self.first_line_column_offset = columns;
117 }
118
119 pub fn try_map_span(&self, span: SourceSpan) -> Option<SourceSpan> {
120 if !self.text.is_char_boundary(span.start) || !self.text.is_char_boundary(span.end) {
121 return None;
122 }
123 self.edit_map.try_map_span(span)
124 }
125
126 pub(super) fn try_map_enclosing_span(&self, span: SourceSpan) -> Option<SourceSpan> {
127 if !self.text.is_char_boundary(span.start) || !self.text.is_char_boundary(span.end) {
128 return None;
129 }
130 self.edit_map.try_map_enclosing_span(span)
131 }
132
133 pub(crate) fn global_expected_syntax(&self) -> &[EditorExpectedSyntax] {
134 &self.global_expected_syntax
135 }
136
137 pub(crate) fn global_directive_prefixes(&self) -> &[String] {
138 &self.global_directive_prefixes
139 }
140
141 pub(crate) const fn recovered_incomplete_directive(&self) -> bool {
142 self.recovered_incomplete_directive
143 }
144
145 pub(super) fn mark_recovered_incomplete_directive(&mut self) {
146 self.recovered_incomplete_directive = true;
147 }
148
149 pub(super) fn record_global_directive_prefix(&mut self, prefix: String) {
150 if !self.global_directive_prefixes.contains(&prefix) {
151 self.global_directive_prefixes.push(prefix);
152 }
153 }
154
155 pub(super) fn record_global_expected_syntax(
156 &mut self,
157 kind: EditorExpectedSyntaxKind,
158 span: SourceSpan,
159 ) {
160 let Some(span) = self.try_map_span(span) else {
161 return;
162 };
163 self.global_expected_syntax
164 .push(EditorExpectedSyntax::new(kind, span));
165 }
166
167 pub fn into_text(self) -> String {
168 self.text
169 }
170
171 pub(super) fn apply_edits(
172 &mut self,
173 edits: Vec<SourceEdit>,
174 control: &OperationControl,
175 ) -> OperationControlResult<()> {
176 let mut checkpoints = ControlledEditRebuildCheckpoints::new(control)?;
177 if edits.is_empty() {
178 return Ok(());
179 }
180 assert!(
183 edits_are_sorted_controlled(&edits, &mut checkpoints)?,
184 "source edits must be sorted by range"
185 );
186 let (replacement_bytes, removed_bytes) =
187 assert_valid_edits_controlled(&self.text, &edits, &mut checkpoints)?;
188
189 let old_text = &self.text;
192 let old_map = &self.edit_map;
193 let mut text = String::with_capacity(
194 old_text
195 .len()
196 .saturating_sub(removed_bytes)
197 .saturating_add(replacement_bytes),
198 );
199 let mut builder = EditMapBuilder::new(old_map.original_len);
200 let mut cursor = 0usize;
201
202 for edit in edits {
203 checkpoints.processed_item()?;
204 checkpoints.push_str(&mut text, &old_text[cursor..edit.range.start])?;
205 builder.copy_from(old_map, cursor..edit.range.start, &mut checkpoints)?;
206
207 if edit.replacement.is_empty() {
208 builder.delete_from(old_map, edit.range.clone(), &mut checkpoints)?;
209 } else {
210 checkpoints.push_str(&mut text, &edit.replacement)?;
211 builder.replace_from(
212 old_map,
213 edit.range.clone(),
214 edit.replacement.len(),
215 edit.mapping,
216 &mut checkpoints,
217 )?;
218 }
219 cursor = edit.range.end;
220 }
221
222 checkpoints.push_str(&mut text, &old_text[cursor..])?;
223 builder.copy_from(old_map, cursor..old_text.len(), &mut checkpoints)?;
224 let edit_map = builder.finish(text.len(), &mut checkpoints)?;
225 #[cfg(debug_assertions)]
226 {
227 let well_formed = edit_map.is_well_formed_controlled(&mut checkpoints)?;
228 debug_assert!(well_formed);
229 }
230 checkpoints.finish()?;
231
232 self.text = text;
233 self.edit_map = edit_map;
234 Ok(())
235 }
236
237 #[cfg(test)]
238 fn apply_edits_uncontrolled(&mut self, edits: Vec<SourceEdit>) {
239 self.apply_edits(edits, &OperationControl::new())
240 .expect("a private parse control cannot be cancelled");
241 }
242}
243
244struct ControlledEditRebuildCheckpoints<'a> {
245 control: &'a OperationControl,
246 bytes_since_checkpoint: usize,
247 items_since_checkpoint: usize,
248}
249
250impl<'a> ControlledEditRebuildCheckpoints<'a> {
251 fn new(control: &'a OperationControl) -> OperationControlResult<Self> {
252 control.checkpoint()?;
253 Ok(Self {
254 control,
255 bytes_since_checkpoint: 0,
256 items_since_checkpoint: 0,
257 })
258 }
259
260 fn push_str(&mut self, output: &mut String, value: &str) -> OperationControlResult<()> {
261 let mut start = 0usize;
262 while start < value.len() {
263 let remaining_budget = CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES
264 .saturating_sub(self.bytes_since_checkpoint);
265 if remaining_budget == 0 {
266 self.checkpoint()?;
267 continue;
268 }
269
270 let mut end = start.saturating_add(remaining_budget).min(value.len());
271 while end > start && !value.is_char_boundary(end) {
272 end -= 1;
273 }
274 if end == start {
275 self.checkpoint()?;
276 continue;
277 }
278
279 output.push_str(&value[start..end]);
280 self.bytes_since_checkpoint += end - start;
281 start = end;
282 if self.bytes_since_checkpoint == CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES {
283 self.checkpoint()?;
284 }
285 }
286 Ok(())
287 }
288
289 fn processed_item(&mut self) -> OperationControlResult<()> {
290 self.items_since_checkpoint += 1;
291 if self.items_since_checkpoint == CONTROLLED_EDIT_REBUILD_CHECKPOINT_ITEMS {
292 self.checkpoint()?;
293 }
294 Ok(())
295 }
296
297 fn checkpoint(&mut self) -> OperationControlResult<()> {
298 self.control.checkpoint()?;
299 self.bytes_since_checkpoint = 0;
300 self.items_since_checkpoint = 0;
301 Ok(())
302 }
303
304 fn finish(&self) -> OperationControlResult<()> {
305 self.control.checkpoint()
306 }
307}
308
309#[derive(Debug, Clone)]
310struct SourceEditMap {
311 original_len: usize,
312 output_len: usize,
313 segments: Vec<EditMapSegment>,
314 gaps: Vec<EditMapGap>,
315 unmapped_output_ranges: Vec<Range<usize>>,
318 #[cfg(test)]
319 scan_stats: EditMapScanStats,
320}
321
322impl SourceEditMap {
323 fn identity(source_len: usize) -> Self {
324 let segments = (source_len > 0)
325 .then_some(EditMapSegment {
326 output: 0..source_len,
327 original: 0..source_len,
328 mapping: SegmentMapping::ExactBytes,
329 })
330 .into_iter()
331 .collect();
332 Self {
333 original_len: source_len,
334 output_len: source_len,
335 segments,
336 gaps: Vec::new(),
337 unmapped_output_ranges: Vec::new(),
338 #[cfg(test)]
339 scan_stats: EditMapScanStats::default(),
340 }
341 }
342
343 fn try_map_span(&self, span: SourceSpan) -> Option<SourceSpan> {
348 if span.start > span.end || span.end > self.output_len {
349 return None;
350 }
351 if self.has_unmapped_overlap(span.start, span.end)
352 || (span.start < span.end && self.has_gap_inside(span.start, span.end))
353 {
354 return None;
355 }
356
357 let start = self.original_at_start(span.start)?;
358 let end = if span.start == span.end {
359 start
360 } else {
361 self.original_at_end(span.end)?
362 };
363 (start <= end).then(|| SourceSpan::new(start, end))
364 }
365
366 fn try_map_enclosing_span(&self, span: SourceSpan) -> Option<SourceSpan> {
372 if span.start > span.end
373 || span.end > self.output_len
374 || self.has_unmapped_overlap(span.start, span.end)
375 {
376 return None;
377 }
378
379 let start = self.original_at_start(span.start)?;
380 let end = if span.start == span.end {
381 start
382 } else {
383 self.original_at_end(span.end)?
384 };
385 (start <= end).then(|| SourceSpan::new(start, end))
386 }
387
388 fn original_at_start(&self, offset: usize) -> Option<usize> {
389 if offset > self.output_len {
390 return None;
391 }
392 if let Some(gap) = self.gap_at(offset) {
393 return Some(gap.original_right);
394 }
395 if offset == self.output_len {
396 return match self.segments.last() {
397 Some(segment) => segment.original_at_end(offset),
398 None => Some(self.original_len),
399 };
400 }
401 self.segment_to_right(offset)?.original_at_start(offset)
402 }
403
404 fn original_at_end(&self, offset: usize) -> Option<usize> {
405 if offset > self.output_len {
406 return None;
407 }
408 if let Some(gap) = self.gap_at(offset) {
409 return Some(gap.original_left);
410 }
411 if offset == 0 {
412 return match self.segments.first() {
413 Some(segment) => segment.original_at_start(offset),
414 None => Some(0),
415 };
416 }
417 self.segment_to_left(offset)?.original_at_end(offset)
418 }
419
420 fn segment_to_right(&self, offset: usize) -> Option<&EditMapSegment> {
421 let index = self
422 .segments
423 .partition_point(|segment| segment.output.end <= offset);
424 self.segments
425 .get(index)
426 .filter(|segment| segment.output.start <= offset && offset < segment.output.end)
427 }
428
429 fn segment_to_left(&self, offset: usize) -> Option<&EditMapSegment> {
430 let index = self
431 .segments
432 .partition_point(|segment| segment.output.start < offset)
433 .checked_sub(1)?;
434 self.segments
435 .get(index)
436 .filter(|segment| segment.output.start < offset && offset <= segment.output.end)
437 }
438
439 fn gap_at(&self, offset: usize) -> Option<&EditMapGap> {
440 self.gaps
441 .binary_search_by_key(&offset, |gap| gap.output_offset)
442 .ok()
443 .and_then(|index| self.gaps.get(index))
444 }
445
446 fn has_gap_inside(&self, start: usize, end: usize) -> bool {
447 let first = self.gaps.partition_point(|gap| gap.output_offset <= start);
448 self.gaps
449 .get(first)
450 .is_some_and(|gap| gap.output_offset < end)
451 }
452
453 fn has_unmapped_overlap(&self, start: usize, end: usize) -> bool {
454 let first = self
455 .unmapped_output_ranges
456 .partition_point(|range| range.end <= start);
457 self.unmapped_output_ranges.get(first).is_some_and(|range| {
458 if start == end {
459 range.start < start && start < range.end
460 } else {
461 range.start < end && start < range.end
462 }
463 })
464 }
465
466 #[cfg(test)]
467 fn unmapped_ranges_match_segments(&self) -> bool {
468 self.unmapped_output_ranges.iter().eq(self
469 .segments
470 .iter()
471 .filter(|segment| segment.mapping == SegmentMapping::Unmapped)
472 .map(|segment| &segment.output))
473 }
474
475 #[cfg(test)]
476 fn is_well_formed(&self) -> bool {
477 let control = OperationControl::new();
478 let mut checkpoints = ControlledEditRebuildCheckpoints::new(&control)
479 .expect("a private parse control cannot be cancelled");
480 self.is_well_formed_controlled(&mut checkpoints)
481 .expect("a private parse control cannot be cancelled")
482 }
483
484 #[cfg(any(test, debug_assertions))]
485 fn is_well_formed_controlled(
486 &self,
487 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
488 ) -> OperationControlResult<bool> {
489 let mut previous_segment: Option<&EditMapSegment> = None;
490 let mut unmapped_index = 0usize;
491 for segment in &self.segments {
492 checkpoints.processed_item()?;
493 if segment.output.start >= segment.output.end {
494 return Ok(false);
495 }
496 if let Some(previous) = previous_segment
497 && (previous.output.end > segment.output.start
498 || previous.original.end > segment.original.start)
499 {
500 return Ok(false);
501 }
502 if segment.mapping == SegmentMapping::Unmapped {
503 if self.unmapped_output_ranges.get(unmapped_index) != Some(&segment.output) {
504 return Ok(false);
505 }
506 unmapped_index += 1;
507 }
508 previous_segment = Some(segment);
509 }
510 if unmapped_index != self.unmapped_output_ranges.len()
511 || self
512 .segments
513 .last()
514 .is_some_and(|segment| segment.output.end > self.output_len)
515 {
516 return Ok(false);
517 }
518
519 let mut previous_gap_offset: Option<usize> = None;
520 for gap in &self.gaps {
521 checkpoints.processed_item()?;
522 if previous_gap_offset.is_some_and(|offset| offset >= gap.output_offset) {
523 return Ok(false);
524 }
525 previous_gap_offset = Some(gap.output_offset);
526 }
527
528 let mut previous_unmapped_end: Option<usize> = None;
529 for range in &self.unmapped_output_ranges {
530 checkpoints.processed_item()?;
531 if previous_unmapped_end.is_some_and(|end| end > range.start) {
532 return Ok(false);
533 }
534 previous_unmapped_end = Some(range.end);
535 }
536 Ok(true)
537 }
538}
539
540#[derive(Debug, Clone, PartialEq, Eq)]
541struct EditMapSegment {
542 output: Range<usize>,
543 original: Range<usize>,
544 mapping: SegmentMapping,
545}
546
547impl EditMapSegment {
548 fn original_at_start(&self, offset: usize) -> Option<usize> {
549 match self.mapping {
550 SegmentMapping::ExactBytes => self
551 .original
552 .start
553 .checked_add(offset.checked_sub(self.output.start)?),
554 SegmentMapping::Boundaries if offset == self.output.start => Some(self.original.start),
555 SegmentMapping::Boundaries if offset == self.output.end => Some(self.original.end),
556 SegmentMapping::Boundaries => None,
557 SegmentMapping::Unmapped => None,
558 }
559 }
560
561 fn original_at_end(&self, offset: usize) -> Option<usize> {
562 self.original_at_start(offset)
563 }
564}
565
566#[derive(Debug, Clone, Copy, PartialEq, Eq)]
567enum SegmentMapping {
568 ExactBytes,
569 Boundaries,
570 Unmapped,
571}
572
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574struct EditMapGap {
575 output_offset: usize,
576 original_left: usize,
577 original_right: usize,
578}
579
580#[cfg(test)]
581#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
582struct EditMapScanStats {
583 copy_ranges: usize,
584 copy_segment_steps: usize,
585 copy_gap_steps: usize,
586 lookup_segment_advances: usize,
587 lookup_gap_advances: usize,
588 exact_segment_steps: usize,
589 exact_gap_steps: usize,
590}
591
592#[derive(Default)]
598struct EditMapCursor {
599 copy_segment_index: usize,
600 copy_gap_index: usize,
601 lookup_segment_index: usize,
602 lookup_gap_index: usize,
603 exact_segment_index: usize,
604 exact_gap_index: usize,
605 #[cfg(debug_assertions)]
606 last_copy_end: usize,
607 #[cfg(debug_assertions)]
608 last_lookup_offset: usize,
609 #[cfg(debug_assertions)]
610 last_exact_end: usize,
611 #[cfg(test)]
612 scan_stats: EditMapScanStats,
613}
614
615impl EditMapCursor {
616 fn advance_lookup_to(
617 &mut self,
618 old: &SourceEditMap,
619 offset: usize,
620 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
621 ) -> OperationControlResult<()> {
622 #[cfg(debug_assertions)]
623 {
624 debug_assert!(self.last_lookup_offset <= offset);
625 self.last_lookup_offset = offset;
626 }
627
628 while old
629 .segments
630 .get(self.lookup_segment_index)
631 .is_some_and(|segment| segment.output.end <= offset)
632 {
633 checkpoints.processed_item()?;
634 self.lookup_segment_index += 1;
635 #[cfg(test)]
636 {
637 self.scan_stats.lookup_segment_advances += 1;
638 }
639 }
640 while old
641 .gaps
642 .get(self.lookup_gap_index)
643 .is_some_and(|gap| gap.output_offset < offset)
644 {
645 checkpoints.processed_item()?;
646 self.lookup_gap_index += 1;
647 #[cfg(test)]
648 {
649 self.scan_stats.lookup_gap_advances += 1;
650 }
651 }
652 Ok(())
653 }
654
655 fn gap_at_lookup_offset<'a>(
656 &self,
657 old: &'a SourceEditMap,
658 offset: usize,
659 ) -> Option<&'a EditMapGap> {
660 old.gaps
661 .get(self.lookup_gap_index)
662 .filter(|gap| gap.output_offset == offset)
663 }
664
665 fn original_at_start(
666 &mut self,
667 old: &SourceEditMap,
668 offset: usize,
669 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
670 ) -> OperationControlResult<Option<usize>> {
671 if offset > old.output_len {
672 return Ok(None);
673 }
674 self.advance_lookup_to(old, offset, checkpoints)?;
675 if let Some(gap) = self.gap_at_lookup_offset(old, offset) {
676 return Ok(Some(gap.original_right));
677 }
678 if offset == old.output_len {
679 return Ok(match old.segments.last() {
680 Some(segment) => segment.original_at_end(offset),
681 None => Some(old.original_len),
682 });
683 }
684 Ok(old
685 .segments
686 .get(self.lookup_segment_index)
687 .filter(|segment| segment.output.start <= offset && offset < segment.output.end)
688 .and_then(|segment| segment.original_at_start(offset)))
689 }
690
691 fn original_at_end(
692 &mut self,
693 old: &SourceEditMap,
694 offset: usize,
695 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
696 ) -> OperationControlResult<Option<usize>> {
697 if offset > old.output_len {
698 return Ok(None);
699 }
700 self.advance_lookup_to(old, offset, checkpoints)?;
701 if let Some(gap) = self.gap_at_lookup_offset(old, offset) {
702 return Ok(Some(gap.original_left));
703 }
704 if offset == 0 {
705 return Ok(match old.segments.first() {
706 Some(segment) => segment.original_at_start(offset),
707 None => Some(0),
708 });
709 }
710 if let Some(segment) = old.segments.get(self.lookup_segment_index)
711 && segment.output.start < offset
712 && offset < segment.output.end
713 {
714 return Ok(segment.original_at_end(offset));
715 }
716 Ok(self
717 .lookup_segment_index
718 .checked_sub(1)
719 .and_then(|index| old.segments.get(index))
720 .filter(|segment| segment.output.start < offset && offset <= segment.output.end)
721 .and_then(|segment| segment.original_at_end(offset)))
722 }
723
724 fn original_anchor(
725 &mut self,
726 old: &SourceEditMap,
727 offset: usize,
728 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
729 ) -> OperationControlResult<usize> {
730 self.advance_lookup_to(old, offset, checkpoints)?;
731 if let Some(gap) = self.gap_at_lookup_offset(old, offset) {
732 return Ok(gap.original_right);
733 }
734 if offset == old.output_len {
735 return Ok(old.original_len);
736 }
737 Ok(old
738 .segments
739 .get(self.lookup_segment_index)
740 .filter(|segment| segment.output.start <= offset && offset < segment.output.end)
741 .map_or(old.original_len, |segment| segment.original.start))
742 }
743
744 fn range_is_exact_bytes(
745 &mut self,
746 old: &SourceEditMap,
747 range: &Range<usize>,
748 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
749 ) -> OperationControlResult<bool> {
750 #[cfg(debug_assertions)]
751 {
752 debug_assert!(self.last_exact_end <= range.start);
753 self.last_exact_end = range.end;
754 }
755 if range.start > range.end || range.end > old.output_len {
756 return Ok(false);
757 }
758 if range.start == range.end {
759 return Ok(true);
760 }
761
762 while old
763 .segments
764 .get(self.exact_segment_index)
765 .is_some_and(|segment| segment.output.end <= range.start)
766 {
767 checkpoints.processed_item()?;
768 self.exact_segment_index += 1;
769 #[cfg(test)]
770 {
771 self.scan_stats.exact_segment_steps += 1;
772 }
773 }
774 while old
775 .gaps
776 .get(self.exact_gap_index)
777 .is_some_and(|gap| gap.output_offset <= range.start)
778 {
779 checkpoints.processed_item()?;
780 self.exact_gap_index += 1;
781 #[cfg(test)]
782 {
783 self.scan_stats.exact_gap_steps += 1;
784 }
785 }
786 if old
787 .gaps
788 .get(self.exact_gap_index)
789 .is_some_and(|gap| gap.output_offset < range.end)
790 {
791 checkpoints.processed_item()?;
792 #[cfg(test)]
793 {
794 self.scan_stats.exact_gap_steps += 1;
795 }
796 return Ok(false);
797 }
798
799 let mut output_offset = range.start;
800 while output_offset < range.end {
801 let Some(segment) = old.segments.get(self.exact_segment_index) else {
802 return Ok(false);
803 };
804 checkpoints.processed_item()?;
805 #[cfg(test)]
806 {
807 self.scan_stats.exact_segment_steps += 1;
808 }
809 if segment.output.start > output_offset || segment.mapping != SegmentMapping::ExactBytes
810 {
811 return Ok(false);
812 }
813 output_offset = segment.output.end.min(range.end);
814 if segment.output.end <= range.end {
815 self.exact_segment_index += 1;
816 }
817 }
818 Ok(true)
819 }
820}
821
822struct EditMapBuilder {
823 original_len: usize,
824 output_len: usize,
825 segments: Vec<EditMapSegment>,
826 gaps: Vec<EditMapGap>,
827 cursor: EditMapCursor,
828}
829
830impl EditMapBuilder {
831 fn new(original_len: usize) -> Self {
832 Self {
833 original_len,
834 output_len: 0,
835 segments: Vec::new(),
836 gaps: Vec::new(),
837 cursor: EditMapCursor::default(),
838 }
839 }
840
841 fn copy_from(
842 &mut self,
843 old: &SourceEditMap,
844 range: Range<usize>,
845 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
846 ) -> OperationControlResult<()> {
847 if range.start >= range.end {
848 return Ok(());
849 }
850 #[cfg(debug_assertions)]
851 {
852 debug_assert!(self.cursor.last_copy_end <= range.start);
853 self.cursor.last_copy_end = range.end;
854 }
855 #[cfg(test)]
856 {
857 self.cursor.scan_stats.copy_ranges += 1;
858 }
859 let output_base = self.output_len;
860
861 while old
862 .segments
863 .get(self.cursor.copy_segment_index)
864 .is_some_and(|segment| segment.output.end <= range.start)
865 {
866 checkpoints.processed_item()?;
867 #[cfg(test)]
868 {
869 self.cursor.scan_stats.copy_segment_steps += 1;
870 }
871 self.cursor.copy_segment_index += 1;
872 }
873 while let Some(segment) = old.segments.get(self.cursor.copy_segment_index) {
874 if segment.output.start >= range.end {
875 break;
876 }
877 checkpoints.processed_item()?;
878 #[cfg(test)]
879 {
880 self.cursor.scan_stats.copy_segment_steps += 1;
881 }
882 let start = segment.output.start.max(range.start);
883 let end = segment.output.end.min(range.end);
884 debug_assert!(start < end);
885
886 let (original, mapping) = match segment.mapping {
887 SegmentMapping::ExactBytes => {
888 let original_start = segment.original.start + (start - segment.output.start);
889 (
890 original_start..original_start + (end - start),
891 SegmentMapping::ExactBytes,
892 )
893 }
894 SegmentMapping::Boundaries
895 if start == segment.output.start && end == segment.output.end =>
896 {
897 (segment.original.clone(), SegmentMapping::Boundaries)
898 }
899 SegmentMapping::Boundaries | SegmentMapping::Unmapped => {
900 let anchor = segment.original.start;
901 (anchor..anchor, SegmentMapping::Unmapped)
902 }
903 };
904 self.push_segment(EditMapSegment {
905 output: output_base + (start - range.start)..output_base + (end - range.start),
906 original,
907 mapping,
908 });
909 if segment.output.end <= range.end {
910 self.cursor.copy_segment_index += 1;
911 } else {
912 break;
913 }
914 }
915
916 while old
917 .gaps
918 .get(self.cursor.copy_gap_index)
919 .is_some_and(|gap| gap.output_offset < range.start)
920 {
921 checkpoints.processed_item()?;
922 #[cfg(test)]
923 {
924 self.cursor.scan_stats.copy_gap_steps += 1;
925 }
926 self.cursor.copy_gap_index += 1;
927 }
928 while let Some(gap) = old.gaps.get(self.cursor.copy_gap_index) {
929 if gap.output_offset > range.end {
930 break;
931 }
932 checkpoints.processed_item()?;
933 #[cfg(test)]
934 {
935 self.cursor.scan_stats.copy_gap_steps += 1;
936 }
937 self.push_gap(EditMapGap {
938 output_offset: output_base + (gap.output_offset - range.start),
939 original_left: gap.original_left,
940 original_right: gap.original_right,
941 });
942 if gap.output_offset == range.end {
943 break;
944 }
945 self.cursor.copy_gap_index += 1;
946 }
947 self.output_len += range.end - range.start;
948 Ok(())
949 }
950
951 fn delete_from(
952 &mut self,
953 old: &SourceEditMap,
954 range: Range<usize>,
955 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
956 ) -> OperationControlResult<()> {
957 let left = match self.cursor.original_at_end(old, range.start, checkpoints)? {
958 Some(offset) => offset,
959 None => self.cursor.original_anchor(old, range.start, checkpoints)?,
960 };
961 let right = match self.cursor.original_at_start(old, range.end, checkpoints)? {
962 Some(offset) => offset,
963 None => self.cursor.original_anchor(old, range.end, checkpoints)?,
964 };
965 self.push_gap(EditMapGap {
966 output_offset: self.output_len,
967 original_left: left,
968 original_right: right,
969 });
970 Ok(())
971 }
972
973 fn replace_from(
974 &mut self,
975 old: &SourceEditMap,
976 range: Range<usize>,
977 replacement_len: usize,
978 mapping: ReplacementMapping,
979 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
980 ) -> OperationControlResult<()> {
981 let original_start = self
982 .cursor
983 .original_at_start(old, range.start, checkpoints)?;
984 let start_is_mappable = original_start.is_some();
985 let original_start = match original_start {
986 Some(offset) => offset,
987 None => self.cursor.original_anchor(old, range.start, checkpoints)?,
988 };
989 let original_end = self.cursor.original_at_end(old, range.end, checkpoints)?;
990 let endpoints_are_mappable = start_is_mappable && original_end.is_some();
991 let original_end = original_end.unwrap_or(original_start);
992 let exact = mapping == ReplacementMapping::ExactBytes
993 && endpoints_are_mappable
994 && replacement_len == range.end - range.start
995 && self.cursor.range_is_exact_bytes(old, &range, checkpoints)?
996 && original_end.checked_sub(original_start) == Some(replacement_len);
997 self.push_segment(EditMapSegment {
998 output: self.output_len..self.output_len + replacement_len,
999 original: original_start..original_end,
1000 mapping: if !endpoints_are_mappable {
1001 SegmentMapping::Unmapped
1002 } else if exact {
1003 SegmentMapping::ExactBytes
1004 } else {
1005 SegmentMapping::Boundaries
1006 },
1007 });
1008 self.output_len += replacement_len;
1009 Ok(())
1010 }
1011
1012 fn push_segment(&mut self, segment: EditMapSegment) {
1013 if let Some(last) = self.segments.last_mut()
1014 && last.mapping == SegmentMapping::ExactBytes
1015 && segment.mapping == SegmentMapping::ExactBytes
1016 && last.output.end == segment.output.start
1017 && last.original.end == segment.original.start
1018 {
1019 last.output.end = segment.output.end;
1020 last.original.end = segment.original.end;
1021 return;
1022 }
1023 self.segments.push(segment);
1024 }
1025
1026 fn push_gap(&mut self, gap: EditMapGap) {
1027 if let Some(last) = self.gaps.last_mut() {
1028 assert!(
1029 last.output_offset <= gap.output_offset,
1030 "source edit map gaps must be emitted in output order"
1031 );
1032 if last.output_offset == gap.output_offset {
1033 last.original_left = last.original_left.min(gap.original_left);
1034 last.original_right = last.original_right.max(gap.original_right);
1035 return;
1036 }
1037 }
1038 self.gaps.push(gap);
1039 }
1040
1041 fn finish(
1042 self,
1043 output_len: usize,
1044 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
1045 ) -> OperationControlResult<SourceEditMap> {
1046 debug_assert_eq!(self.output_len, output_len);
1047 let segments = self.segments;
1048 let mut unmapped_output_ranges = Vec::new();
1049 for segment in &segments {
1050 checkpoints.processed_item()?;
1051 if segment.mapping == SegmentMapping::Unmapped {
1052 unmapped_output_ranges.push(segment.output.clone());
1053 }
1054 }
1055 Ok(SourceEditMap {
1056 original_len: self.original_len,
1057 output_len,
1058 segments,
1059 gaps: self.gaps,
1060 unmapped_output_ranges,
1061 #[cfg(test)]
1062 scan_stats: self.cursor.scan_stats,
1063 })
1064 }
1065}
1066
1067fn edits_are_sorted_controlled(
1068 edits: &[SourceEdit],
1069 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
1070) -> OperationControlResult<bool> {
1071 for pair in edits.windows(2) {
1072 checkpoints.processed_item()?;
1073 if (pair[0].range.start, pair[0].range.end) > (pair[1].range.start, pair[1].range.end) {
1074 return Ok(false);
1075 }
1076 }
1077 Ok(true)
1078}
1079
1080fn assert_valid_edits_controlled(
1081 source: &str,
1082 edits: &[SourceEdit],
1083 checkpoints: &mut ControlledEditRebuildCheckpoints<'_>,
1084) -> OperationControlResult<(usize, usize)> {
1085 let mut cursor = 0usize;
1086 let mut replacement_bytes = 0usize;
1087 let mut removed_bytes = 0usize;
1088 for edit in edits {
1089 checkpoints.processed_item()?;
1090 assert!(edit.range.start <= edit.range.end, "reversed source edit");
1091 assert!(edit.range.start >= cursor, "overlapping source edits");
1092 assert!(edit.range.end <= source.len(), "source edit out of bounds");
1093 assert!(source.is_char_boundary(edit.range.start));
1094 assert!(source.is_char_boundary(edit.range.end));
1095 replacement_bytes = replacement_bytes.saturating_add(edit.replacement.len());
1096 removed_bytes = removed_bytes.saturating_add(edit.range.end - edit.range.start);
1097 cursor = edit.range.end;
1098 }
1099 Ok((replacement_bytes, removed_bytes))
1100}
1101
1102#[cfg(test)]
1103mod tests {
1104 use super::*;
1105 use std::fmt::Write as _;
1106
1107 #[test]
1108 fn composes_deletions_replacements_and_unicode_without_global_degradation() {
1109 let original = "前A\r\nREMOVE中#quot;后";
1110 let mut source = PreprocessedSource::new(original);
1111 let cr = source.text().find('\r').unwrap();
1112 source.apply_edits_uncontrolled(vec![SourceEdit::replace(
1113 cr..cr + 2,
1114 "\n",
1115 ReplacementMapping::Boundaries,
1116 )]);
1117 let remove = source.text().find("REMOVE").unwrap();
1118 source.apply_edits_uncontrolled(vec![SourceEdit::delete(remove..remove + "REMOVE".len())]);
1119 let hash = source.text().find('#').unwrap();
1120 source.apply_edits_uncontrolled(vec![
1121 SourceEdit::replace(hash..hash + 1, "fl°", ReplacementMapping::Boundaries),
1122 SourceEdit::replace(
1123 hash + "#quot".len()..hash + "#quot;".len(),
1124 "¶ß",
1125 ReplacementMapping::Boundaries,
1126 ),
1127 ]);
1128
1129 assert_eq!(source.text(), "前A\n中fl°quot¶ß后");
1130 let middle = source.text().find('中').unwrap();
1131 let mapped = source
1132 .try_map_span(SourceSpan::new(middle, middle + '中'.len_utf8()))
1133 .unwrap();
1134 assert_eq!(&original[mapped.start..mapped.end], "中");
1135
1136 let entity = source.text().find("fl°quot¶ß").unwrap();
1137 let mapped = source
1138 .try_map_span(SourceSpan::new(entity, entity + "fl°quot¶ß".len()))
1139 .unwrap();
1140 assert_eq!(&original[mapped.start..mapped.end], "#quot;");
1141
1142 let crossing_deletion = source.text().find('A').unwrap();
1143 let after_deletion = source.text().find('中').unwrap() + '中'.len_utf8();
1144 assert_eq!(
1145 source.try_map_span(SourceSpan::new(crossing_deletion, after_deletion)),
1146 None
1147 );
1148 }
1149
1150 #[test]
1151 fn enclosing_mapping_preserves_provenance_across_deleted_indentation() {
1152 let original = " first\n second\n";
1153 let mut source = PreprocessedSource::new(original);
1154 let second_indent = original.find(" second").expect("second-line indentation");
1155 source.apply_edits_uncontrolled(vec![
1156 SourceEdit::delete(0..2),
1157 SourceEdit::delete(second_indent..second_indent + 2),
1158 ]);
1159
1160 let span = SourceSpan::new(0, "first\nsecond".len());
1161 assert_eq!(source.try_map_span(span), None);
1162 let enclosing = source
1163 .try_map_enclosing_span(span)
1164 .expect("provenance boundaries remain mappable");
1165 assert_eq!(&original[enclosing.start..enclosing.end], "first\n second");
1166 }
1167
1168 #[test]
1169 fn boundary_replacements_only_reject_locally_ambiguous_offsets() {
1170 let mut source = PreprocessedSource::new("A#x;B");
1171 source.apply_edits_uncontrolled(vec![
1172 SourceEdit::replace(1..2, "fl°", ReplacementMapping::Boundaries),
1173 SourceEdit::replace(3..4, "¶ß", ReplacementMapping::Boundaries),
1174 ]);
1175 assert_eq!(source.text(), "Afl°x¶ßB");
1176
1177 let b = source.text().find('B').unwrap();
1178 assert_eq!(
1179 source.try_map_span(SourceSpan::new(b, b + 1)),
1180 Some(SourceSpan::new(4, 5))
1181 );
1182 assert_eq!(source.try_map_span(SourceSpan::new(2, 2)), None);
1183
1184 let identity = PreprocessedSource::new("A😀B");
1185 assert_eq!(identity.try_map_span(SourceSpan::new(2, 2)), None);
1186 }
1187
1188 #[test]
1189 fn controlled_text_rebuild_cancellation_preserves_the_previous_source_atomically() {
1190 let original = format!(
1191 "{}TAIL",
1192 "a".repeat(CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES * 2)
1193 );
1194 let mut source = PreprocessedSource::new(&original);
1195 let before = source.clone();
1196 let tail = source.text().len() - "TAIL".len();
1197 let control = OperationControl::new();
1198 control.cancel_after_checkpoints(1);
1199
1200 let result = source.apply_edits(
1201 vec![SourceEdit::replace(
1202 tail..tail + "TAIL".len(),
1203 "tail",
1204 ReplacementMapping::ExactBytes,
1205 )],
1206 &control,
1207 );
1208
1209 assert!(result.is_err());
1210 assert!(control.is_cancelled());
1211 assert_source_state_eq(&source, &before);
1212 }
1213
1214 #[test]
1215 fn controlled_mapping_rebuild_cancellation_preserves_the_previous_source_atomically() {
1216 let original = "x".repeat(CONTROLLED_EDIT_REBUILD_CHECKPOINT_ITEMS * 2);
1217 let mut source = PreprocessedSource::new(&original);
1218 source.apply_edits_uncontrolled(
1219 (0..original.len())
1220 .map(|offset| {
1221 SourceEdit::replace(offset..offset + 1, "x", ReplacementMapping::Boundaries)
1222 })
1223 .collect(),
1224 );
1225 assert!(source.edit_map.segments.len() > CONTROLLED_EDIT_REBUILD_CHECKPOINT_ITEMS);
1226 assert!(source.text().len() < CONTROLLED_EDIT_REBUILD_CHECKPOINT_BYTES);
1227
1228 let before = source.clone();
1229 let control = OperationControl::new();
1230 control.cancel_after_checkpoints(1);
1231 let last_byte = source.text().len() - 1;
1232 let result =
1233 source.apply_edits(vec![SourceEdit::delete(last_byte..last_byte + 1)], &control);
1234
1235 assert!(result.is_err());
1236 assert!(control.is_cancelled());
1237 assert_source_state_eq(&source, &before);
1238 }
1239
1240 #[test]
1241 fn same_offset_insertions_and_adjacent_deletions_coalesce_gaps_in_order() {
1242 let mut source = PreprocessedSource::new("aXbYc");
1243 source.apply_edits_uncontrolled(vec![SourceEdit::delete(1..2)]);
1244
1245 source.apply_edits_uncontrolled(vec![
1246 SourceEdit::replace(1..1, "<", ReplacementMapping::Boundaries),
1247 SourceEdit::replace(1..1, ">", ReplacementMapping::Boundaries),
1248 SourceEdit::delete(1..2),
1249 SourceEdit::delete(2..3),
1250 ]);
1251
1252 assert_eq!(source.text(), "a<>c");
1253 assert!(source.edit_map.is_well_formed());
1254 assert_eq!(source.edit_map.gaps.len(), 2);
1255
1256 let c = source.text().find('c').unwrap();
1257 assert_eq!(
1258 source.try_map_span(SourceSpan::new(c, c + 1)),
1259 Some(SourceSpan::new(4, 5))
1260 );
1261 assert_eq!(source.try_map_span(SourceSpan::new(0, c + 1)), None);
1262 }
1263
1264 #[test]
1265 fn composing_an_edit_inside_generated_text_marks_only_that_region_unmappable() {
1266 let mut source = PreprocessedSource::new("A#x;B");
1267 source.apply_edits_uncontrolled(vec![SourceEdit::replace(
1268 1..2,
1269 "fl°",
1270 ReplacementMapping::Boundaries,
1271 )]);
1272 let degree = source.text().find('°').unwrap();
1273 source.apply_edits_uncontrolled(vec![SourceEdit::replace(
1274 degree..degree + '°'.len_utf8(),
1275 "!",
1276 ReplacementMapping::ExactBytes,
1277 )]);
1278 assert!(source.edit_map.unmapped_ranges_match_segments());
1279 assert!(!source.edit_map.unmapped_output_ranges.is_empty());
1280
1281 let generated = source.text().find('fl').unwrap();
1282 assert_eq!(
1283 source.try_map_span(SourceSpan::new(generated, generated + 'fl'.len_utf8())),
1284 None
1285 );
1286 let b = source.text().find('B').unwrap();
1287 assert_eq!(
1288 source.try_map_span(SourceSpan::new(b, b + 1)),
1289 Some(SourceSpan::new(4, 5))
1290 );
1291 }
1292
1293 #[test]
1294 fn crlf_comment_batches_scan_old_map_once_and_preserve_offsets() {
1295 const MIN_INPUT_BYTES: usize = 1024 * 1024;
1296
1297 let mut original = String::with_capacity(MIN_INPUT_BYTES + 256);
1298 let mut pair_count = 0usize;
1299 while original.len() < MIN_INPUT_BYTES {
1300 let group = if pair_count.is_multiple_of(2) {
1301 "even"
1302 } else {
1303 "odd"
1304 };
1305 write!(
1306 original,
1307 "KEEP-{pair_count:05} payload\r\n%% {group}-comment-{pair_count:05} payload\r\n"
1308 )
1309 .unwrap();
1310 pair_count += 1;
1311 }
1312 original.push_str("TAIL\r\n");
1313 assert!(original.len() >= MIN_INPUT_BYTES);
1314
1315 let mut source = PreprocessedSource::new(&original);
1316 source.apply_edits_uncontrolled(crlf_normalization_edits(source.text()));
1317 assert!(!source.text().contains('\r'));
1318 assert!(
1319 source.edit_map.unmapped_output_ranges.is_empty(),
1320 "CRLF boundary mappings must not make every fact lookup scan every line"
1321 );
1322
1323 let even_comments = comment_line_deletions(source.text(), "%% even-comment-");
1324 source.apply_edits_uncontrolled(even_comments);
1325
1326 let old_segment_count = source.edit_map.segments.len();
1327 let old_gap_count = source.edit_map.gaps.len();
1328 let odd_comments = comment_line_deletions(source.text(), "%% odd-comment-");
1329 let odd_comment_count = odd_comments.len();
1330 assert!(odd_comment_count > 8_000);
1331 assert!(old_gap_count > 8_000);
1332
1333 source.apply_edits_uncontrolled(odd_comments);
1334
1335 let scans = source.edit_map.scan_stats;
1336 assert_eq!(scans.copy_ranges, odd_comment_count + 1);
1337 assert!(
1338 scans.copy_segment_steps <= old_segment_count + scans.copy_ranges,
1339 "copying revisited old segments: {scans:?} for {old_segment_count} segments"
1340 );
1341 assert!(
1342 scans.copy_gap_steps <= old_gap_count + scans.copy_ranges,
1343 "copying revisited old gaps: {scans:?} for {old_gap_count} gaps"
1344 );
1345 assert!(
1346 scans.lookup_segment_advances <= old_segment_count,
1347 "endpoint lookup revisited old segments: {scans:?}"
1348 );
1349 assert!(
1350 scans.lookup_gap_advances <= old_gap_count,
1351 "endpoint lookup revisited old gaps: {scans:?}"
1352 );
1353
1354 assert!(!source.text().contains("%%"));
1355 for pair_index in [0, pair_count / 2, pair_count - 1] {
1356 let literal = format!("KEEP-{pair_index:05}");
1357 assert_literal_mapping(&source, &original, &literal, &literal);
1358 }
1359
1360 let first_keep = "KEEP-00000 payload";
1361 let output_newline = source.text().find(first_keep).unwrap() + first_keep.len();
1362 let original_newline = original.find(first_keep).unwrap() + first_keep.len();
1363 assert_eq!(
1364 source.try_map_span(SourceSpan::new(output_newline, output_newline + 1)),
1365 Some(SourceSpan::new(original_newline, original_newline + 2))
1366 );
1367
1368 let second_keep = source.text().find("KEEP-00001").unwrap();
1369 assert_eq!(
1370 source.try_map_span(SourceSpan::new(output_newline, second_keep + 1)),
1371 None,
1372 "a span crossing a removed comment must remain unmappable"
1373 );
1374 }
1375
1376 #[test]
1377 fn seeded_edit_compositions_preserve_exact_monotonic_source_ranges() {
1378 const SEED: u64 = 0x4d45_524d_414e_5538;
1379 const CASES: usize = 64;
1380 let original = "前|KEEP_A|COPY|DELETE|\r\n|#quot;|<span title=\"值😀\">|WIDE|KEEP_B|后";
1381 let mut rng = DeterministicRng::new(SEED);
1382
1383 for case in 0..CASES {
1384 let mut source = PreprocessedSource::new(original);
1385 for transformation in shuffled_transformations(&mut rng) {
1386 apply_transformation(&mut source, transformation);
1387 assert_mapping_invariants(&source, original, case, transformation);
1388 }
1389
1390 assert_literal_mapping(&source, original, "KEEP_A", "KEEP_A");
1391 assert_literal_mapping(&source, original, "copy", "COPY");
1392 assert_literal_mapping(&source, original, "\n", "\r\n");
1393 assert_literal_mapping(
1394 &source,
1395 original,
1396 "title="值😀"",
1397 "title=\"值😀\"",
1398 );
1399 assert_literal_mapping(&source, original, "长值😀", "WIDE");
1400 assert_literal_mapping(&source, original, "KEEP_B", "KEEP_B");
1401
1402 let crossing_start = source.text().find("copy").unwrap();
1403 let crossing_end = source.text().find('\n').unwrap() + 1;
1404 assert_eq!(
1405 source.try_map_span(SourceSpan::new(crossing_start, crossing_end)),
1406 None,
1407 "case {case}: a span crossing deleted source must stay locally unmappable"
1408 );
1409
1410 if case % 2 == 0 {
1411 assert_literal_mapping(&source, original, "fl°quot¶ß", "#quot;");
1412 } else {
1413 let degree = source.text().find('°').unwrap();
1414 source.apply_edits_uncontrolled(vec![SourceEdit::delete(
1415 degree..degree + '°'.len_utf8(),
1416 )]);
1417 assert_mapping_invariants(
1418 &source,
1419 original,
1420 case,
1421 SeededTransformation::GeneratedInteriorDeletion,
1422 );
1423 let generated = source.text().find("flquot¶ß").unwrap();
1424 assert_eq!(
1425 source.try_map_span(SourceSpan::new(generated, generated + "flquot¶ß".len(),)),
1426 None,
1427 "case {case}: editing generated bytes must not invent an original range"
1428 );
1429 assert_literal_mapping(&source, original, "KEEP_B", "KEEP_B");
1430 }
1431 }
1432 }
1433
1434 #[derive(Debug, Clone, Copy)]
1435 enum SeededTransformation {
1436 ExactByteReplacement,
1437 Deletion,
1438 CrLfNormalization,
1439 EntityEncoding,
1440 QuoteExpansion,
1441 BoundaryReplacement,
1442 GeneratedInteriorDeletion,
1443 }
1444
1445 struct DeterministicRng(u64);
1446
1447 impl DeterministicRng {
1448 fn new(seed: u64) -> Self {
1449 Self(seed)
1450 }
1451
1452 fn index(&mut self, upper: usize) -> usize {
1453 self.0 ^= self.0 << 13;
1454 self.0 ^= self.0 >> 7;
1455 self.0 ^= self.0 << 17;
1456 (self.0 as usize) % upper
1457 }
1458 }
1459
1460 fn shuffled_transformations(rng: &mut DeterministicRng) -> [SeededTransformation; 6] {
1461 let mut transformations = [
1462 SeededTransformation::ExactByteReplacement,
1463 SeededTransformation::Deletion,
1464 SeededTransformation::CrLfNormalization,
1465 SeededTransformation::EntityEncoding,
1466 SeededTransformation::QuoteExpansion,
1467 SeededTransformation::BoundaryReplacement,
1468 ];
1469 for index in (1..transformations.len()).rev() {
1470 transformations.swap(index, rng.index(index + 1));
1471 }
1472 transformations
1473 }
1474
1475 fn apply_transformation(source: &mut PreprocessedSource, transformation: SeededTransformation) {
1476 match transformation {
1477 SeededTransformation::ExactByteReplacement => {
1478 replace_once(source, "COPY", "copy", ReplacementMapping::ExactBytes);
1479 }
1480 SeededTransformation::Deletion => {
1481 let start = source.text().find("DELETE|").unwrap();
1482 source.apply_edits_uncontrolled(vec![SourceEdit::delete(
1483 start..start + "DELETE|".len(),
1484 )]);
1485 }
1486 SeededTransformation::CrLfNormalization => {
1487 replace_once(source, "\r\n", "\n", ReplacementMapping::Boundaries);
1488 }
1489 SeededTransformation::EntityEncoding => {
1490 let start = source.text().find("#quot;").unwrap();
1491 source.apply_edits_uncontrolled(vec![
1492 SourceEdit::replace(start..start + 1, "fl°", ReplacementMapping::Boundaries),
1493 SourceEdit::replace(
1494 start + "#quot".len()..start + "#quot;".len(),
1495 "¶ß",
1496 ReplacementMapping::Boundaries,
1497 ),
1498 ]);
1499 }
1500 SeededTransformation::QuoteExpansion => {
1501 let edits = source
1502 .text()
1503 .match_indices('"')
1504 .map(|(start, _)| {
1505 SourceEdit::replace(
1506 start..start + 1,
1507 """,
1508 ReplacementMapping::Boundaries,
1509 )
1510 })
1511 .collect();
1512 source.apply_edits_uncontrolled(edits);
1513 }
1514 SeededTransformation::BoundaryReplacement => {
1515 replace_once(source, "WIDE", "长值😀", ReplacementMapping::Boundaries);
1516 }
1517 SeededTransformation::GeneratedInteriorDeletion => unreachable!(),
1518 }
1519 }
1520
1521 fn replace_once(
1522 source: &mut PreprocessedSource,
1523 needle: &str,
1524 replacement: &str,
1525 mapping: ReplacementMapping,
1526 ) {
1527 let start = source.text().find(needle).unwrap();
1528 source.apply_edits_uncontrolled(vec![SourceEdit::replace(
1529 start..start + needle.len(),
1530 replacement,
1531 mapping,
1532 )]);
1533 }
1534
1535 fn crlf_normalization_edits(text: &str) -> Vec<SourceEdit> {
1536 text.match_indices("\r\n")
1537 .map(|(start, _)| {
1538 SourceEdit::replace(start..start + 2, "\n", ReplacementMapping::Boundaries)
1539 })
1540 .collect()
1541 }
1542
1543 fn comment_line_deletions(text: &str, prefix: &str) -> Vec<SourceEdit> {
1544 let mut line_start = 0usize;
1545 text.split_inclusive('\n')
1546 .filter_map(|line| {
1547 let range = line_start..line_start + line.len();
1548 line_start = range.end;
1549 line.starts_with(prefix).then(|| SourceEdit::delete(range))
1550 })
1551 .collect()
1552 }
1553
1554 fn assert_literal_mapping(
1555 source: &PreprocessedSource,
1556 original: &str,
1557 output_literal: &str,
1558 original_literal: &str,
1559 ) {
1560 let output_start = source.text().find(output_literal).unwrap();
1561 let original_start = original.find(original_literal).unwrap();
1562 assert_eq!(
1563 source.try_map_span(SourceSpan::new(
1564 output_start,
1565 output_start + output_literal.len(),
1566 )),
1567 Some(SourceSpan::new(
1568 original_start,
1569 original_start + original_literal.len(),
1570 )),
1571 "{output_literal:?} must map exactly to {original_literal:?}"
1572 );
1573 }
1574
1575 fn assert_source_state_eq(actual: &PreprocessedSource, expected: &PreprocessedSource) {
1576 assert_eq!(actual.text, expected.text);
1577 assert_eq!(actual.edit_map.original_len, expected.edit_map.original_len);
1578 assert_eq!(actual.edit_map.output_len, expected.edit_map.output_len);
1579 assert_eq!(actual.edit_map.segments, expected.edit_map.segments);
1580 assert_eq!(actual.edit_map.gaps, expected.edit_map.gaps);
1581 assert_eq!(
1582 actual.edit_map.unmapped_output_ranges,
1583 expected.edit_map.unmapped_output_ranges
1584 );
1585 assert_eq!(actual.edit_map.scan_stats, expected.edit_map.scan_stats);
1586 assert_eq!(
1587 actual.global_expected_syntax,
1588 expected.global_expected_syntax
1589 );
1590 assert_eq!(
1591 actual.global_directive_prefixes,
1592 expected.global_directive_prefixes
1593 );
1594 assert_eq!(
1595 actual.recovered_incomplete_directive,
1596 expected.recovered_incomplete_directive
1597 );
1598 }
1599
1600 fn assert_mapping_invariants(
1601 source: &PreprocessedSource,
1602 original: &str,
1603 case: usize,
1604 transformation: SeededTransformation,
1605 ) {
1606 assert!(
1607 source.edit_map.is_well_formed(),
1608 "case {case} after {transformation:?}: malformed composed edit map"
1609 );
1610 assert_eq!(source.edit_map.original_len, original.len());
1611 assert_eq!(source.edit_map.output_len, source.text().len());
1612
1613 let mut boundaries = source
1614 .text()
1615 .char_indices()
1616 .map(|(offset, _)| offset)
1617 .collect::<Vec<_>>();
1618 boundaries.push(source.text().len());
1619
1620 let mut previous_original_offset = None;
1621 for &offset in &boundaries {
1622 if let Some(mapped) = source.try_map_span(SourceSpan::new(offset, offset)) {
1623 assert_eq!(mapped.start, mapped.end);
1624 assert!(mapped.end <= original.len());
1625 assert!(original.is_char_boundary(mapped.start));
1626 if let Some(previous) = previous_original_offset {
1627 assert!(
1628 previous <= mapped.start,
1629 "case {case} after {transformation:?}: output boundary mapping regressed"
1630 );
1631 }
1632 previous_original_offset = Some(mapped.start);
1633 }
1634 }
1635
1636 for (start_index, &start) in boundaries.iter().enumerate() {
1637 for &end in &boundaries[start_index..] {
1638 let Some(mapped) = source.try_map_span(SourceSpan::new(start, end)) else {
1639 continue;
1640 };
1641 assert!(mapped.start <= mapped.end);
1642 assert!(mapped.end <= original.len());
1643 assert!(original.is_char_boundary(mapped.start));
1644 assert!(original.is_char_boundary(mapped.end));
1645 }
1646 }
1647
1648 assert_eq!(
1649 source.try_map_span(SourceSpan::new(0, source.text().len() + 1)),
1650 None
1651 );
1652 let emoji = source.text().find('😀').unwrap();
1653 assert_eq!(
1654 source.try_map_span(SourceSpan::new(emoji + 1, emoji + 1)),
1655 None
1656 );
1657 }
1658}