feat(span): port over more methods from TextRange (#3592)

Ports over more utility methods from `TextRange`, as per feedback in
[this
comment](https://github.com/oxc-project/oxc/pull/3589#discussion_r1632169132)
from #3589
This commit is contained in:
Don Isaac 2024-06-09 12:59:00 -04:00 committed by GitHub
parent f0b689d115
commit 129f91e444
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 187 additions and 6 deletions

View file

@ -1,10 +1,16 @@
use std::{borrow::Borrow, fmt, hash, ops::Deref};
use std::{
borrow::Borrow,
fmt, hash,
ops::{Deref, Index},
};
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer};
use compact_str::CompactString;
use crate::Span;
#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
@ -12,7 +18,7 @@ export type Atom = string;
export type CompactStr = string;
"#;
/// Maximum length for inline string, which can be created with `CompactStr::new_const`.
/// Maximum length for inline string, which can be created with [`CompactStr::new_const`].
pub const MAX_INLINE_LEN: usize = 16;
/// An inlinable string for oxc_allocator.
@ -261,6 +267,14 @@ impl PartialEq<CompactStr> for &str {
}
}
impl Index<Span> for CompactStr {
type Output = str;
fn index(&self, index: Span) -> &Self::Output {
&self.0[index]
}
}
impl hash::Hash for CompactStr {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher);

View file

@ -1,7 +1,10 @@
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
// Silence erroneous warnings from Rust Analyzer for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use std::hash::{Hash, Hasher};
use std::{
hash::{Hash, Hasher},
ops::{Index, IndexMut, Range},
};
use miette::{LabeledSpan, SourceOffset, SourceSpan};
@ -56,6 +59,35 @@ impl Span {
Self { start, end }
}
/// Create a new empty [`Span`] that starts and ends at an offset position.
///
/// # Examples
/// ```
/// use oxc_span::Span;
///
/// let fifth = Span::empty(5);
/// assert!(fifth.is_empty());
/// assert_eq!(fifth, Span::sized(5, 0));
/// assert_eq!(fifth, Span::new(5, 5));
/// ```
pub fn empty(at: u32) -> Self {
Self { start: at, end: at }
}
/// Create a new [`Span`] starting at `start` and covering `size` characters.
///
/// # Example
/// ```
/// use oxc_span::Span;
///
/// let span = Span::sized(2, 4);
/// assert_eq!(span.size(), 4);
/// assert_eq!(span, Span::new(2, 6));
/// ```
pub const fn sized(start: u32, size: u32) -> Self {
Self::new(start, start + size)
}
/// Get the number of characters covered by the [`Span`].
///
/// # Example
@ -66,7 +98,7 @@ impl Span {
/// assert_eq!(Span::new(0, 5).size(), 5);
/// assert_eq!(Span::new(5, 10).size(), 5);
/// ```
pub fn size(&self) -> u32 {
pub const fn size(&self) -> u32 {
debug_assert!(self.start <= self.end);
self.end - self.start
}
@ -81,7 +113,7 @@ impl Span {
/// assert!(Span::new(5, 5).is_empty());
/// assert!(!Span::new(0, 5).is_empty());
/// ```
pub fn is_empty(&self) -> bool {
pub const fn is_empty(&self) -> bool {
debug_assert!(self.start <= self.end);
self.start == self.end
}
@ -102,6 +134,119 @@ impl Span {
Self::new(self.start.min(other.start), self.end.max(other.end))
}
/// Create a [`Span`] that has its start position moved to the left by
/// `offset` bytes.
///
/// # Example
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(5, 10);
/// assert_eq!(a.expand_left(5), Span::new(0, 10));
/// ```
///
/// ## Bounds
///
/// The leftmost bound of the span is clamped to 0. It is safe to call this
/// method with a value larger than the start position.
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(0, 5);
/// assert_eq!(a.expand_left(5), Span::new(0, 5));
/// ```
#[must_use]
pub const fn expand_left(self, offset: u32) -> Self {
Self::new(self.start.saturating_sub(offset), self.end)
}
/// Create a [`Span`] that has its start position moved to the right by
/// `offset` bytes.
///
/// It is a logical error to shrink the start of the [`Span`] past its end
/// position.
///
/// # Example
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(5, 10);
/// let shrunk = a.shrink_left(5);
/// assert_eq!(shrunk, Span::new(10, 10));
///
/// // Shrinking past the end of the span is a logical error that will panic
/// // in debug builds.
/// std::panic::catch_unwind(|| {
/// shrunk.shrink_left(5);
/// });
/// ```
///
#[must_use]
pub const fn shrink_left(self, offset: u32) -> Self {
let start = self.start.saturating_add(offset);
debug_assert!(start <= self.end);
Self::new(self.start.saturating_add(offset), self.end)
}
/// Create a [`Span`] that has its end position moved to the right by
/// `offset` bytes.
///
/// # Example
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(5, 10);
/// assert_eq!(a.expand_right(5), Span::new(5, 15));
/// ```
///
/// ## Bounds
///
/// The rightmost bound of the span is clamped to `u32::MAX`. It is safe to
/// call this method with a value larger than the end position.
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(0, u32::MAX);
/// assert_eq!(a.expand_right(5), Span::new(0, u32::MAX));
/// ```
#[must_use]
pub const fn expand_right(self, offset: u32) -> Self {
Self::new(self.start, self.end.saturating_add(offset))
}
/// Create a [`Span`] that has its end position moved to the left by
/// `offset` bytes.
///
/// It is a logical error to shrink the end of the [`Span`] past its start
/// position.
///
/// # Example
///
/// ```
/// use oxc_span::Span;
///
/// let a = Span::new(5, 10);
/// let shrunk = a.shrink_right(5);
/// assert_eq!(shrunk, Span::new(5, 5));
///
/// // Shrinking past the start of the span is a logical error that will panic
/// // in debug builds.
/// std::panic::catch_unwind(|| {
/// shrunk.shrink_right(5);
/// });
/// ```
#[must_use]
pub const fn shrink_right(self, offset: u32) -> Self {
let end = self.end.saturating_sub(offset);
debug_assert!(self.start <= end);
Self::new(self.start, end)
}
/// Get a snippet of text from a source string that the [`Span`] covers.
///
/// # Example
@ -118,6 +263,28 @@ impl Span {
}
}
impl Index<Span> for str {
type Output = str;
#[inline]
fn index(&self, index: Span) -> &Self::Output {
&self[index.start as usize..index.end as usize]
}
}
impl IndexMut<Span> for str {
#[inline]
fn index_mut(&mut self, index: Span) -> &mut Self::Output {
&mut self[index.start as usize..index.end as usize]
}
}
impl From<Range<u32>> for Span {
#[inline]
fn from(range: Range<u32>) -> Self {
Self::new(range.start, range.end)
}
}
impl Hash for Span {
fn hash<H: Hasher>(&self, _state: &mut H) {
// hash to nothing so all ast spans can be comparable with hash