Skip to content

Commit 359796f

Browse files
committed
feat(span): add move_right and move_left
1 parent 763a618 commit 359796f

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

crates/oxc_span/src/span.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,63 @@ impl Span {
387387
Self::new(self.start, end)
388388
}
389389

390+
/// Create a [`Span`] that has its start and end position moved to the left by
391+
/// `offset` bytes.
392+
///
393+
/// # Example
394+
///
395+
/// ```
396+
/// use oxc_span::Span;
397+
///
398+
/// let a = Span::new(5, 10);
399+
/// let moved = a.move_left(5);
400+
/// assert_eq!(moved, Span::new(0, 5));
401+
///
402+
/// // Moving the start over 0 is logical error that will panic in debug builds.
403+
/// std::panic::catch_unwind(|| {
404+
/// moved.move_left(5);
405+
/// });
406+
/// ```
407+
#[must_use]
408+
pub const fn move_left(self, offset: u32) -> Self {
409+
let start = self.start.saturating_sub(offset);
410+
#[cfg(debug_assertions)]
411+
if start == 0 {
412+
debug_assert!(self.start == offset, "Cannot move span past zero length");
413+
}
414+
Self::new(start, self.end.saturating_sub(offset))
415+
}
416+
417+
/// Create a [`Span`] that has its start and end position moved to the right by
418+
/// `offset` bytes.
419+
///
420+
/// # Example
421+
///
422+
/// ```
423+
/// use oxc_span::Span;
424+
///
425+
/// let a = Span::new(5, 10);
426+
/// let moved = a.move_right(5);
427+
/// assert_eq!(moved, Span::new(10, 15));
428+
///
429+
/// // Moving the end over `u32::MAX` is logical error that will panic in debug builds.
430+
/// std::panic::catch_unwind(|| {
431+
/// moved.move_right(u32::MAX);
432+
/// });
433+
/// ```
434+
#[must_use]
435+
pub const fn move_right(self, offset: u32) -> Self {
436+
let end = self.end.saturating_add(offset);
437+
#[cfg(debug_assertions)]
438+
if end == u32::MAX {
439+
debug_assert!(
440+
u32::MAX.saturating_sub(offset) == self.end,
441+
"Cannot move span past `u32::MAX` length"
442+
);
443+
}
444+
Self::new(self.start.saturating_add(offset), end)
445+
}
446+
390447
/// Get a snippet of text from a source string that the [`Span`] covers.
391448
///
392449
/// # Example
@@ -715,6 +772,39 @@ mod test {
715772
let span = Span::new(5, 10);
716773
let _ = span.shrink(5);
717774
}
775+
776+
#[test]
777+
fn test_move_left() {
778+
let span = Span::new(5, 10);
779+
assert_eq!(span.move_left(1), Span::new(4, 9));
780+
assert_eq!(span.move_left(2), Span::new(3, 8));
781+
assert_eq!(span.move_left(5), Span::new(0, 5));
782+
}
783+
784+
#[test]
785+
#[should_panic(expected = "Cannot move span past zero length")]
786+
fn test_move_past_start() {
787+
let span = Span::new(5, 10);
788+
let _ = span.move_left(6);
789+
}
790+
791+
#[test]
792+
fn test_move_right() {
793+
let span: Span = Span::new(5, 10);
794+
assert_eq!(span.move_right(1), Span::new(6, 11));
795+
assert_eq!(span.move_right(2), Span::new(7, 12));
796+
assert_eq!(
797+
span.move_right(u32::MAX.saturating_sub(10)),
798+
Span::new(u32::MAX.saturating_sub(5), u32::MAX)
799+
);
800+
}
801+
802+
#[test]
803+
#[should_panic(expected = "Cannot move span past `u32::MAX` length")]
804+
fn test_move_past_end() {
805+
let span = Span::new(u32::MAX.saturating_sub(2), u32::MAX.saturating_sub(1));
806+
let _ = span.move_right(2);
807+
}
718808
}
719809

720810
#[cfg(test)]

0 commit comments

Comments
 (0)