Replace row/column based Location with byte-offsets. (#3931)

This commit is contained in:
Micha Reiser 2023-04-26 20:11:02 +02:00 committed by GitHub
parent ee91598835
commit cab65b25da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
418 changed files with 6203 additions and 7040 deletions

View file

@ -1,19 +0,0 @@
[package]
name = "ruff_text_size"
version = "0.0.0"
publish = false
edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
serde = { workspace = true, optional = true }
schemars = { workspace = true, optional = true }
[dev-dependencies]
serde_test = { version = "1.0.152" }
static_assertions = { version = "1.1.0" }
[[test]]
name = "serde"
path = "tests/serde.rs"
required-features = ["serde"]

View file

@ -1,34 +0,0 @@
//! Newtypes for working with text sizes/ranges in a more type-safe manner.
//!
//! This library can help with two things:
//! * Reducing storage requirements for offsets and ranges, under the
//! assumption that 32 bits is enough.
//! * Providing standard vocabulary types for applications where text ranges
//! are pervasive.
//!
//! However, you should not use this library simply because you work with
//! strings. In the overwhelming majority of cases, using `usize` and
//! `std::ops::Range<usize>` is better. In particular, if you are publishing a
//! library, using only std types in the interface would make it more
//! interoperable. Similarly, if you are writing something like a lexer, which
//! produces, but does not *store* text ranges, then sticking to `usize` would
//! be better.
//!
//! Minimal Supported Rust Version: latest stable.
#![forbid(unsafe_code)]
#![warn(missing_debug_implementations, missing_docs)]
mod range;
mod size;
mod traits;
#[cfg(feature = "schemars")]
mod schemars_impls;
#[cfg(feature = "serde")]
mod serde_impls;
pub use crate::{range::TextRange, size::TextSize, traits::TextLen};
#[cfg(target_pointer_width = "16")]
compile_error!("text-size assumes usize >= u32 and does not work on 16-bit targets");

View file

@ -1,537 +0,0 @@
use cmp::Ordering;
use {
crate::TextSize,
std::{
cmp, fmt,
ops::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign},
},
};
/// A range in text, represented as a pair of [`TextSize`][struct@TextSize].
///
/// It is a logic error for `start` to be greater than `end`.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TextRange {
// Invariant: start <= end
start: TextSize,
end: TextSize,
}
impl fmt::Debug for TextRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..{}", self.start().raw, self.end().raw)
}
}
impl TextRange {
/// Creates a new `TextRange` with the given `start` and `end` (`start..end`).
///
/// # Panics
///
/// Panics if `end < start`.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let start = TextSize::from(5);
/// let end = TextSize::from(10);
/// let range = TextRange::new(start, end);
///
/// assert_eq!(range.start(), start);
/// assert_eq!(range.end(), end);
/// assert_eq!(range.len(), end - start);
/// ```
#[inline]
pub fn new(start: TextSize, end: TextSize) -> TextRange {
assert!(start <= end);
TextRange { start, end }
}
/// Create a new `TextRange` with the given `offset` and `len` (`offset..offset + len`).
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let text = "0123456789";
///
/// let offset = TextSize::from(2);
/// let length = TextSize::from(5);
/// let range = TextRange::at(offset, length);
///
/// assert_eq!(range, TextRange::new(offset, offset + length));
/// assert_eq!(&text[range], "23456")
/// ```
#[inline]
pub fn at(offset: TextSize, len: TextSize) -> TextRange {
TextRange::new(offset, offset + len)
}
/// Create a zero-length range at the specified offset (`offset..offset`).
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let point: TextSize;
/// # point = TextSize::from(3);
/// let range = TextRange::empty(point);
/// assert!(range.is_empty());
/// assert_eq!(range, TextRange::new(point, point));
/// ```
#[inline]
pub fn empty(offset: TextSize) -> TextRange {
TextRange {
start: offset,
end: offset,
}
}
/// Create a range up to the given end (`..end`).
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let point: TextSize;
/// # point = TextSize::from(12);
/// let range = TextRange::up_to(point);
///
/// assert_eq!(range.len(), point);
/// assert_eq!(range, TextRange::new(0.into(), point));
/// assert_eq!(range, TextRange::at(0.into(), point));
/// ```
#[inline]
pub fn up_to(end: TextSize) -> TextRange {
TextRange {
start: 0.into(),
end,
}
}
}
/// Identity methods.
impl TextRange {
/// The start point of this range.
#[inline]
pub const fn start(self) -> TextSize {
self.start
}
/// The end point of this range.
#[inline]
pub const fn end(self) -> TextSize {
self.end
}
/// The size of this range.
#[inline]
pub const fn len(self) -> TextSize {
// HACK for const fn: math on primitives only
TextSize {
raw: self.end().raw - self.start().raw,
}
}
/// Check if this range is empty.
#[inline]
pub const fn is_empty(self) -> bool {
// HACK for const fn: math on primitives only
self.start().raw == self.end().raw
}
}
/// Manipulation methods.
impl TextRange {
/// Check if this range contains an offset.
///
/// The end index is considered excluded.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let (start, end): (TextSize, TextSize);
/// # start = 10.into(); end = 20.into();
/// let range = TextRange::new(start, end);
/// assert!(range.contains(start));
/// assert!(!range.contains(end));
/// ```
#[inline]
pub fn contains(self, offset: TextSize) -> bool {
self.start() <= offset && offset < self.end()
}
/// Check if this range contains an offset.
///
/// The end index is considered included.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let (start, end): (TextSize, TextSize);
/// # start = 10.into(); end = 20.into();
/// let range = TextRange::new(start, end);
/// assert!(range.contains_inclusive(start));
/// assert!(range.contains_inclusive(end));
/// ```
#[inline]
pub fn contains_inclusive(self, offset: TextSize) -> bool {
self.start() <= offset && offset <= self.end()
}
/// Check if this range completely contains another range.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let larger = TextRange::new(0.into(), 20.into());
/// let smaller = TextRange::new(5.into(), 15.into());
/// assert!(larger.contains_range(smaller));
/// assert!(!smaller.contains_range(larger));
///
/// // a range always contains itself
/// assert!(larger.contains_range(larger));
/// assert!(smaller.contains_range(smaller));
/// ```
#[inline]
pub fn contains_range(self, other: TextRange) -> bool {
self.start() <= other.start() && other.end() <= self.end()
}
/// The range covered by both ranges, if it exists.
/// If the ranges touch but do not overlap, the output range is empty.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// assert_eq!(
/// TextRange::intersect(
/// TextRange::new(0.into(), 10.into()),
/// TextRange::new(5.into(), 15.into()),
/// ),
/// Some(TextRange::new(5.into(), 10.into())),
/// );
/// ```
#[inline]
pub fn intersect(self, other: TextRange) -> Option<TextRange> {
let start = cmp::max(self.start(), other.start());
let end = cmp::min(self.end(), other.end());
if end < start {
return None;
}
Some(TextRange::new(start, end))
}
/// Extends the range to cover `other` as well.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// assert_eq!(
/// TextRange::cover(
/// TextRange::new(0.into(), 5.into()),
/// TextRange::new(15.into(), 20.into()),
/// ),
/// TextRange::new(0.into(), 20.into()),
/// );
/// ```
#[inline]
#[must_use]
pub fn cover(self, other: TextRange) -> TextRange {
let start = cmp::min(self.start(), other.start());
let end = cmp::max(self.end(), other.end());
TextRange::new(start, end)
}
/// Extends the range to cover `other` offsets as well.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// assert_eq!(
/// TextRange::empty(0.into()).cover_offset(20.into()),
/// TextRange::new(0.into(), 20.into()),
/// )
/// ```
#[inline]
#[must_use]
pub fn cover_offset(self, offset: TextSize) -> TextRange {
self.cover(TextRange::empty(offset))
}
/// Add an offset to this range.
///
/// Note that this is not appropriate for changing where a `TextRange` is
/// within some string; rather, it is for changing the reference anchor
/// that the `TextRange` is measured against.
///
/// The unchecked version (`Add::add`) will _always_ panic on overflow,
/// in contrast to primitive integers, which check in debug mode only.
#[inline]
pub fn checked_add(self, offset: TextSize) -> Option<TextRange> {
Some(TextRange {
start: self.start.checked_add(offset)?,
end: self.end.checked_add(offset)?,
})
}
/// Subtract an offset from this range.
///
/// Note that this is not appropriate for changing where a `TextRange` is
/// within some string; rather, it is for changing the reference anchor
/// that the `TextRange` is measured against.
///
/// The unchecked version (`Sub::sub`) will _always_ panic on overflow,
/// in contrast to primitive integers, which check in debug mode only.
#[inline]
pub fn checked_sub(self, offset: TextSize) -> Option<TextRange> {
Some(TextRange {
start: self.start.checked_sub(offset)?,
end: self.end.checked_sub(offset)?,
})
}
/// Relative order of the two ranges (overlapping ranges are considered
/// equal).
///
///
/// This is useful when, for example, binary searching an array of disjoint
/// ranges.
///
/// # Examples
///
/// ```
/// # use ruff_text_size::*;
/// # use std::cmp::Ordering;
///
/// let a = TextRange::new(0.into(), 3.into());
/// let b = TextRange::new(4.into(), 5.into());
/// assert_eq!(a.ordering(b), Ordering::Less);
///
/// let a = TextRange::new(0.into(), 3.into());
/// let b = TextRange::new(3.into(), 5.into());
/// assert_eq!(a.ordering(b), Ordering::Less);
///
/// let a = TextRange::new(0.into(), 3.into());
/// let b = TextRange::new(2.into(), 5.into());
/// assert_eq!(a.ordering(b), Ordering::Equal);
///
/// let a = TextRange::new(0.into(), 3.into());
/// let b = TextRange::new(2.into(), 2.into());
/// assert_eq!(a.ordering(b), Ordering::Equal);
///
/// let a = TextRange::new(2.into(), 3.into());
/// let b = TextRange::new(2.into(), 2.into());
/// assert_eq!(a.ordering(b), Ordering::Greater);
/// ```
#[inline]
pub fn ordering(self, other: TextRange) -> Ordering {
if self.end() <= other.start() {
Ordering::Less
} else if other.end() <= self.start() {
Ordering::Greater
} else {
Ordering::Equal
}
}
/// Subtracts an offset from the start position.
///
///
/// ## Panics
/// If `start - amount` is less than zero.
///
/// ## Examples
///
/// ```
/// use ruff_text_size::{TextRange, TextSize};
///
/// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
/// assert_eq!(range.sub_start(TextSize::from(2)), TextRange::new(TextSize::from(3), TextSize::from(10)));
/// ```
#[inline]
#[must_use]
pub fn sub_start(&self, amount: TextSize) -> TextRange {
TextRange::new(self.start() - amount, self.end())
}
/// Adds an offset to the start position.
///
/// ## Panics
/// If `start + amount > end`
///
/// ## Examples
///
/// ```
/// use ruff_text_size::{TextRange, TextSize};
///
/// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
/// assert_eq!(range.add_start(TextSize::from(3)), TextRange::new(TextSize::from(8), TextSize::from(10)));
/// ```
#[inline]
#[must_use]
pub fn add_start(&self, amount: TextSize) -> TextRange {
TextRange::new(self.start() + amount, self.end())
}
/// Subtracts an offset from the end position.
///
///
/// ## Panics
/// If `end - amount < 0` or `end - amount < start`
///
/// ## Examples
///
/// ```
/// use ruff_text_size::{TextRange, TextSize};
///
/// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
/// assert_eq!(range.sub_end(TextSize::from(2)), TextRange::new(TextSize::from(5), TextSize::from(8)));
/// ```
#[inline]
#[must_use]
pub fn sub_end(&self, amount: TextSize) -> TextRange {
TextRange::new(self.start(), self.end() - amount)
}
/// Adds an offset to the end position.
///
///
/// ## Panics
/// If `end + amount > u32::MAX`
///
/// ## Examples
///
/// ```
/// use ruff_text_size::{TextRange, TextSize};
///
/// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
/// assert_eq!(range.add_end(TextSize::from(2)), TextRange::new(TextSize::from(5), TextSize::from(12)));
/// ```
#[inline]
#[must_use]
pub fn add_end(&self, amount: TextSize) -> TextRange {
TextRange::new(self.start(), self.end() + amount)
}
}
impl Index<TextRange> for str {
type Output = str;
#[inline]
fn index(&self, index: TextRange) -> &str {
&self[Range::<usize>::from(index)]
}
}
impl Index<TextRange> for String {
type Output = str;
#[inline]
fn index(&self, index: TextRange) -> &str {
&self[Range::<usize>::from(index)]
}
}
impl IndexMut<TextRange> for str {
#[inline]
fn index_mut(&mut self, index: TextRange) -> &mut str {
&mut self[Range::<usize>::from(index)]
}
}
impl IndexMut<TextRange> for String {
#[inline]
fn index_mut(&mut self, index: TextRange) -> &mut str {
&mut self[Range::<usize>::from(index)]
}
}
impl RangeBounds<TextSize> for TextRange {
fn start_bound(&self) -> Bound<&TextSize> {
Bound::Included(&self.start)
}
fn end_bound(&self) -> Bound<&TextSize> {
Bound::Excluded(&self.end)
}
}
impl<T> From<TextRange> for Range<T>
where
T: From<TextSize>,
{
#[inline]
fn from(r: TextRange) -> Self {
r.start().into()..r.end().into()
}
}
macro_rules! ops {
(impl $Op:ident for TextRange by fn $f:ident = $op:tt) => {
impl $Op<&TextSize> for TextRange {
type Output = TextRange;
#[inline]
fn $f(self, other: &TextSize) -> TextRange {
self $op *other
}
}
impl<T> $Op<T> for &TextRange
where
TextRange: $Op<T, Output=TextRange>,
{
type Output = TextRange;
#[inline]
fn $f(self, other: T) -> TextRange {
*self $op other
}
}
};
}
impl Add<TextSize> for TextRange {
type Output = TextRange;
#[inline]
fn add(self, offset: TextSize) -> TextRange {
self.checked_add(offset)
.expect("TextRange +offset overflowed")
}
}
impl Sub<TextSize> for TextRange {
type Output = TextRange;
#[inline]
fn sub(self, offset: TextSize) -> TextRange {
self.checked_sub(offset)
.expect("TextRange -offset overflowed")
}
}
ops!(impl Add for TextRange by fn add = +);
ops!(impl Sub for TextRange by fn sub = -);
impl<A> AddAssign<A> for TextRange
where
TextRange: Add<A, Output = TextRange>,
{
#[inline]
fn add_assign(&mut self, rhs: A) {
*self = *self + rhs;
}
}
impl<S> SubAssign<S> for TextRange
where
TextRange: Sub<S, Output = TextRange>,
{
#[inline]
fn sub_assign(&mut self, rhs: S) {
*self = *self - rhs;
}
}

View file

@ -1,33 +0,0 @@
//! This module implements the [`JsonSchema`] trait from the `schemars` crate for
//! [`TextSize`] and [`TextRange`] if the `schemars` feature is enabled. This trait
//! exposes meta-information on how a given type is serialized and deserialized
//! using `serde`, and is currently used to generate autocomplete information
//! for the `rome.json` configuration file and TypeScript types for the node.js
//! bindings to the Workspace API
use crate::{TextRange, TextSize};
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
impl JsonSchema for TextSize {
fn schema_name() -> String {
String::from("TextSize")
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
// TextSize is represented as a raw u32, see serde_impls.rs for the
// actual implementation
<u32>::json_schema(gen)
}
}
impl JsonSchema for TextRange {
fn schema_name() -> String {
String::from("TextRange")
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
// TextSize is represented as (TextSize, TextSize), see serde_impls.rs
// for the actual implementation
<(TextSize, TextSize)>::json_schema(gen)
}
}

View file

@ -1,47 +0,0 @@
use {
crate::{TextRange, TextSize},
serde::{de, Deserialize, Deserializer, Serialize, Serializer},
};
impl Serialize for TextSize {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.raw.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TextSize {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
u32::deserialize(deserializer).map(TextSize::from)
}
}
impl Serialize for TextRange {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(self.start(), self.end()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TextRange {
#[allow(clippy::nonminimal_bool)]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (start, end) = Deserialize::deserialize(deserializer)?;
if !(start <= end) {
return Err(de::Error::custom(format!(
"invalid range: {start:?}..{end:?}"
)));
}
Ok(TextRange::new(start, end))
}
}

View file

@ -1,161 +0,0 @@
use {
crate::TextLen,
std::{
convert::TryFrom,
fmt, iter,
num::TryFromIntError,
ops::{Add, AddAssign, Sub, SubAssign},
u32,
},
};
/// A measure of text length. Also, equivalently, an index into text.
///
/// This is a UTF-8 bytes offset stored as `u32`, but
/// most clients should treat it as an opaque measure.
///
/// For cases that need to escape `TextSize` and return to working directly
/// with primitive integers, `TextSize` can be converted losslessly to/from
/// `u32` via [`From`] conversions as well as losslessly be converted [`Into`]
/// `usize`. The `usize -> TextSize` direction can be done via [`TryFrom`].
///
/// These escape hatches are primarily required for unit testing and when
/// converting from UTF-8 size to another coordinate space, such as UTF-16.
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TextSize {
pub(crate) raw: u32,
}
impl fmt::Debug for TextSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.raw)
}
}
impl TextSize {
/// The text size of some primitive text-like object.
///
/// Accepts `char`, `&str`, and `&String`.
///
/// # Examples
///
/// ```rust
/// # use ruff_text_size::*;
/// let char_size = TextSize::of('🦀');
/// assert_eq!(char_size, TextSize::from(4));
///
/// let str_size = TextSize::of("rust-analyzer");
/// assert_eq!(str_size, TextSize::from(13));
/// ```
#[inline]
pub fn of<T: TextLen>(text: T) -> TextSize {
text.text_len()
}
}
/// Methods to act like a primitive integer type, where reasonably applicable.
// Last updated for parity with Rust 1.42.0.
impl TextSize {
/// Checked addition. Returns `None` if overflow occurred.
#[inline]
pub fn checked_add(self, rhs: TextSize) -> Option<TextSize> {
self.raw.checked_add(rhs.raw).map(|raw| TextSize { raw })
}
/// Checked subtraction. Returns `None` if overflow occurred.
#[inline]
pub fn checked_sub(self, rhs: TextSize) -> Option<TextSize> {
self.raw.checked_sub(rhs.raw).map(|raw| TextSize { raw })
}
}
impl From<u32> for TextSize {
#[inline]
fn from(raw: u32) -> Self {
TextSize { raw }
}
}
impl From<TextSize> for u32 {
#[inline]
fn from(value: TextSize) -> Self {
value.raw
}
}
impl TryFrom<usize> for TextSize {
type Error = TryFromIntError;
#[inline]
fn try_from(value: usize) -> Result<Self, TryFromIntError> {
Ok(u32::try_from(value)?.into())
}
}
impl From<TextSize> for usize {
#[inline]
fn from(value: TextSize) -> Self {
value.raw as usize
}
}
macro_rules! ops {
(impl $Op:ident for TextSize by fn $f:ident = $op:tt) => {
impl $Op<TextSize> for TextSize {
type Output = TextSize;
#[inline]
fn $f(self, other: TextSize) -> TextSize {
TextSize { raw: self.raw $op other.raw }
}
}
impl $Op<&TextSize> for TextSize {
type Output = TextSize;
#[inline]
fn $f(self, other: &TextSize) -> TextSize {
self $op *other
}
}
impl<T> $Op<T> for &TextSize
where
TextSize: $Op<T, Output=TextSize>,
{
type Output = TextSize;
#[inline]
fn $f(self, other: T) -> TextSize {
*self $op other
}
}
};
}
ops!(impl Add for TextSize by fn add = +);
ops!(impl Sub for TextSize by fn sub = -);
impl<A> AddAssign<A> for TextSize
where
TextSize: Add<A, Output = TextSize>,
{
#[inline]
fn add_assign(&mut self, rhs: A) {
*self = *self + rhs;
}
}
impl<S> SubAssign<S> for TextSize
where
TextSize: Sub<S, Output = TextSize>,
{
#[inline]
fn sub_assign(&mut self, rhs: S) {
*self = *self - rhs;
}
}
impl<A> iter::Sum<A> for TextSize
where
TextSize: Add<A, Output = TextSize>,
{
#[inline]
fn sum<I: Iterator<Item = A>>(iter: I) -> TextSize {
iter.fold(0.into(), Add::add)
}
}

View file

@ -1,37 +0,0 @@
use {crate::TextSize, std::convert::TryInto};
use priv_in_pub::Sealed;
mod priv_in_pub {
pub trait Sealed {}
}
/// Primitives with a textual length that can be passed to [`TextSize::of`].
pub trait TextLen: Copy + Sealed {
/// The textual length of this primitive.
fn text_len(self) -> TextSize;
}
impl Sealed for &'_ str {}
impl TextLen for &'_ str {
#[inline]
fn text_len(self) -> TextSize {
self.len().try_into().unwrap()
}
}
impl Sealed for &'_ String {}
impl TextLen for &'_ String {
#[inline]
fn text_len(self) -> TextSize {
self.as_str().text_len()
}
}
impl Sealed for char {}
impl TextLen for char {
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn text_len(self) -> TextSize {
(self.len_utf8() as u32).into()
}
}

View file

@ -1,18 +0,0 @@
use {
ruff_text_size::{TextRange, TextSize},
static_assertions::assert_impl_all,
std::{
fmt::Debug,
hash::Hash,
marker::{Send, Sync},
panic::{RefUnwindSafe, UnwindSafe},
},
};
// auto traits
assert_impl_all!(TextSize: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe);
assert_impl_all!(TextRange: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe);
// common traits
assert_impl_all!(TextSize: Copy, Debug, Default, Hash, Ord);
assert_impl_all!(TextRange: Copy, Debug, Default, Hash, Eq);

View file

@ -1,24 +0,0 @@
use ruff_text_size::TextSize;
#[derive(Copy, Clone)]
struct BadRope<'a>(&'a [&'a str]);
impl BadRope<'_> {
fn text_len(self) -> TextSize {
self.0.iter().copied().map(TextSize::of).sum()
}
}
#[test]
fn main() {
let x: char = 'c';
_ = TextSize::of(x);
let x: &str = "hello";
_ = TextSize::of(x);
let x: &String = &"hello".into();
_ = TextSize::of(x);
_ = BadRope(&[""]).text_len();
}

View file

@ -1,8 +0,0 @@
use ruff_text_size::TextRange;
#[test]
fn main() {
let range = TextRange::default();
_ = &""[range];
_ = &String::new()[range];
}

View file

@ -1,79 +0,0 @@
use {
ruff_text_size::{TextRange, TextSize},
std::ops,
};
fn size(x: u32) -> TextSize {
TextSize::from(x)
}
fn range(x: ops::Range<u32>) -> TextRange {
TextRange::new(x.start.into(), x.end.into())
}
#[test]
fn sum() {
let xs: Vec<TextSize> = vec![size(0), size(1), size(2)];
assert_eq!(xs.iter().sum::<TextSize>(), size(3));
assert_eq!(xs.into_iter().sum::<TextSize>(), size(3));
}
#[test]
fn math() {
assert_eq!(size(10) + size(5), size(15));
assert_eq!(size(10) - size(5), size(5));
}
#[test]
fn checked_math() {
assert_eq!(size(1).checked_add(size(1)), Some(size(2)));
assert_eq!(size(1).checked_sub(size(1)), Some(size(0)));
assert_eq!(size(1).checked_sub(size(2)), None);
assert_eq!(size(!0).checked_add(size(1)), None);
}
#[test]
#[rustfmt::skip]
fn contains() {
assert!( range(2..4).contains_range(range(2..3)));
assert!( ! range(2..4).contains_range(range(1..3)));
}
#[test]
fn intersect() {
assert_eq!(range(1..2).intersect(range(2..3)), Some(range(2..2)));
assert_eq!(range(1..5).intersect(range(2..3)), Some(range(2..3)));
assert_eq!(range(1..2).intersect(range(3..4)), None);
}
#[test]
fn cover() {
assert_eq!(range(1..2).cover(range(2..3)), range(1..3));
assert_eq!(range(1..5).cover(range(2..3)), range(1..5));
assert_eq!(range(1..2).cover(range(4..5)), range(1..5));
}
#[test]
fn cover_offset() {
assert_eq!(range(1..3).cover_offset(size(0)), range(0..3));
assert_eq!(range(1..3).cover_offset(size(1)), range(1..3));
assert_eq!(range(1..3).cover_offset(size(2)), range(1..3));
assert_eq!(range(1..3).cover_offset(size(3)), range(1..3));
assert_eq!(range(1..3).cover_offset(size(4)), range(1..4));
}
#[test]
#[rustfmt::skip]
fn contains_point() {
assert!( ! range(1..3).contains(size(0)));
assert!( range(1..3).contains(size(1)));
assert!( range(1..3).contains(size(2)));
assert!( ! range(1..3).contains(size(3)));
assert!( ! range(1..3).contains(size(4)));
assert!( ! range(1..3).contains_inclusive(size(0)));
assert!( range(1..3).contains_inclusive(size(1)));
assert!( range(1..3).contains_inclusive(size(2)));
assert!( range(1..3).contains_inclusive(size(3)));
assert!( ! range(1..3).contains_inclusive(size(4)));
}

View file

@ -1,83 +0,0 @@
use {
ruff_text_size::{TextRange, TextSize},
serde_test::{assert_de_tokens_error, assert_tokens, Token},
std::ops,
};
fn size(x: u32) -> TextSize {
TextSize::from(x)
}
fn range(x: ops::Range<u32>) -> TextRange {
TextRange::new(x.start.into(), x.end.into())
}
#[test]
fn size_serialization() {
assert_tokens(&size(00), &[Token::U32(00)]);
assert_tokens(&size(10), &[Token::U32(10)]);
assert_tokens(&size(20), &[Token::U32(20)]);
assert_tokens(&size(30), &[Token::U32(30)]);
}
#[test]
fn range_serialization() {
assert_tokens(
&range(00..10),
&[
Token::Tuple { len: 2 },
Token::U32(00),
Token::U32(10),
Token::TupleEnd,
],
);
assert_tokens(
&range(10..20),
&[
Token::Tuple { len: 2 },
Token::U32(10),
Token::U32(20),
Token::TupleEnd,
],
);
assert_tokens(
&range(20..30),
&[
Token::Tuple { len: 2 },
Token::U32(20),
Token::U32(30),
Token::TupleEnd,
],
);
assert_tokens(
&range(30..40),
&[
Token::Tuple { len: 2 },
Token::U32(30),
Token::U32(40),
Token::TupleEnd,
],
);
}
#[test]
fn invalid_range_deserialization() {
assert_tokens::<TextRange>(
&range(62..92),
&[
Token::Tuple { len: 2 },
Token::U32(62),
Token::U32(92),
Token::TupleEnd,
],
);
assert_de_tokens_error::<TextRange>(
&[
Token::Tuple { len: 2 },
Token::U32(92),
Token::U32(62),
Token::TupleEnd,
],
"invalid range: 92..62",
);
}