Unnamed repository; edit this file 'description' to name the repository.
Merge rust-analyzer/text-size#18
18: minor improvements everywhere r=matklad a=CAD97
Because none of this was quite specific enough to break into its own PR
- Update translation docs
- Officially `compiler_error!`-forbid `cfg(target_pointer_width = "16")`
- `TextRange::before` (alt options: `TextRange::to`, `TextRange::up_to`, `TextRangeTo`, `TextSize::prefix`)
- `TextRange::after` (alt options: `TextSize::suffix`)
- `TextRange::offset` (alt options: `TextRange::add`, `Add::add`, `TextRange::reanchor`)
- `TextSize::one` (alt options: )
- `TextSize::ONE` (alt options: `TextSize::ASCII`
- `#[inline]` capability for a bunch of things
This PR is mainly just to get opinions on all of these little things, then I'll re-force-push with a commit just with those.
Co-authored-by: CAD97 <[email protected]>
| -rw-r--r-- | lib/text-size/src/lib.rs | 3 | ||||
| -rw-r--r-- | lib/text-size/src/range.rs | 51 | ||||
| -rw-r--r-- | lib/text-size/src/size.rs | 41 | ||||
| -rw-r--r-- | lib/text-size/src/traits.rs | 2 |
4 files changed, 65 insertions, 32 deletions
diff --git a/lib/text-size/src/lib.rs b/lib/text-size/src/lib.rs index 32262de12c..e194e2317b 100644 --- a/lib/text-size/src/lib.rs +++ b/lib/text-size/src/lib.rs @@ -13,3 +13,6 @@ mod traits; mod serde_impls; pub use crate::{range::TextRange, size::TextSize, traits::TextSized}; + +#[cfg(target_pointer_width = "16")] +compile_error!("text-size assumes usize >= u32 and does not work on 16-bit targets"); diff --git a/lib/text-size/src/range.rs b/lib/text-size/src/range.rs index df5296af14..789d300665 100644 --- a/lib/text-size/src/range.rs +++ b/lib/text-size/src/range.rs @@ -8,24 +8,19 @@ use { /// A range in text, represented as a pair of [`TextSize`][struct@TextSize]. /// -/// It is a logical error to have `end() < start()`, but -/// code must not assume this is true for `unsafe` guarantees. -/// /// # Translation from `text_unit` /// -/// - `TextRange::from_to(from, to)` ⟹ `TextRange::from(from..to)` -/// - `TextRange::offset_len(offset, size)` ⟹ `TextRange::from(offset..offset + size)` +/// - `TextRange::from_to(from, to)` ⟹ `TextRange(from, to)` +/// - `TextRange::offset_len(offset, size)` ⟹ `TextRange::up_to(size).offset(offset)` /// - `range.start()` ⟹ `range.start()` /// - `range.end()` ⟹ `range.end()` -/// - `range.len()` ⟹ `range.len()`<sup>†</sup> +/// - `range.len()` ⟹ `range.len()` /// - `range.is_empty()` ⟹ `range.is_empty()` -/// - `a.is_subrange(b)` ⟹ `b.contains(a)` +/// - `a.is_subrange(b)` ⟹ `b.contains_range(a)` /// - `a.intersection(b)` ⟹ `TextRange::intersection(a, b)` /// - `a.extend_to(b)` ⟹ `TextRange::covering(a, b)` -/// - `range.contains(offset)` ⟹ `range.contains_exclusive(point)` +/// - `range.contains(offset)` ⟹ `range.contains(point)` /// - `range.contains_inclusive(offset)` ⟹ `range.contains_inclusive(point)` -/// -/// † See the note on [`TextRange::len`] for differing behavior for incorrect reverse ranges. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct TextRange { // Invariant: start <= end @@ -39,47 +34,61 @@ impl fmt::Debug for TextRange { } } -/// Creates a new `TextRange` with given `start` and `end. +/// Creates a new `TextRange` with the given `start` and `end` (`start..end`). /// /// # Panics /// /// Panics if `end < start`. #[allow(non_snake_case)] +#[inline] pub fn TextRange(start: TextSize, end: TextSize) -> TextRange { assert!(start <= end); TextRange { start, end } } -/// Identity methods. impl TextRange { - /// Creates a zero-length range at the specified offset. - pub const fn empty(self, offset: TextSize) -> TextRange { + /// Create a zero-length range at the specified offset (`offset..offset`). + #[inline] + pub const fn empty(offset: TextSize) -> TextRange { TextRange { start: offset, end: offset, } } + /// Create a range up to the given end (`..end`). + #[inline] + pub const fn up_to(end: TextSize) -> TextRange { + TextRange { + start: TextSize::zero(), + 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(self.end().raw - self.start().raw) } - /// Check if this range empty or reversed. - /// - /// When `end() < start()`, this returns false. - /// Code should prefer `is_empty()` to `len() == 0`. + /// 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 @@ -99,8 +108,7 @@ impl TextRange { /// /// The end index is considered included. pub fn contains_inclusive(self, offset: TextSize) -> bool { - let point = offset.into(); - self.start() <= point && point <= self.end() + self.start() <= offset && offset <= self.end() } /// Check if this range completely contains another range. @@ -129,12 +137,14 @@ impl TextRange { impl Index<TextRange> for str { type Output = str; + #[inline] fn index(&self, index: TextRange) -> &Self::Output { &self[Range::<usize>::from(index)] } } impl IndexMut<TextRange> for str { + #[inline] fn index_mut(&mut self, index: TextRange) -> &mut Self::Output { &mut self[Range::<usize>::from(index)] } @@ -154,6 +164,7 @@ impl<T> From<TextRange> for Range<T> where T: From<TextSize>, { + #[inline] fn from(r: TextRange) -> Self { r.start().into()..r.end().into() } diff --git a/lib/text-size/src/size.rs b/lib/text-size/src/size.rs index 454327a015..5239ea6691 100644 --- a/lib/text-size/src/size.rs +++ b/lib/text-size/src/size.rs @@ -11,15 +11,23 @@ use { /// A measure of text length. Also, equivalently, an index into text. /// -/// This is a utf8-bytes-offset stored as `u32`, but +/// 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. +/// /// # Translation from `text_unit` /// /// - `TextUnit::of_char(c)` ⟹ `TextSize::of(c)` -/// - `TextUnit::of_str(s)` ⟹ `TextSize:of(s)` +/// - `TextUnit::of_str(s)` ⟹ `TextSize::of(s)` /// - `TextUnit::from_usize(size)` ⟹ `TextSize::try_from(size).unwrap_or_else(|| panic!(_))` -/// - `unit.to_usize()` ⟹ `usize::try_from(size).unwrap_or_else(|| panic!(_))` +/// - `unit.to_usize()` ⟹ `usize::from(size)` #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TextSize { pub(crate) raw: u32, @@ -38,6 +46,7 @@ impl fmt::Debug for TextSize { impl TextSize { /// The text size of some text-like object. + #[inline] pub fn of(text: impl TextSized) -> TextSize { text.text_size() } @@ -46,6 +55,7 @@ impl TextSize { /// /// This is equivalent to `TextSize::default()` or [`TextSize::MIN`], /// but is more explicit on intent. + #[inline] pub const fn zero() -> TextSize { TextSize(0) } @@ -59,24 +69,28 @@ impl TextSize { /// The largest representable text size. (`u32::MAX`) pub const MAX: TextSize = TextSize(u32::MAX); - #[allow(missing_docs)] + /// Checked addition. Returns `None` if overflow occurred. + #[inline] pub fn checked_add(self, rhs: TextSize) -> Option<TextSize> { self.raw.checked_add(rhs.raw).map(TextSize) } - #[allow(missing_docs)] + /// Checked subtraction. Returns `None` if overflow occurred. + #[inline] pub fn checked_sub(self, rhs: TextSize) -> Option<TextSize> { self.raw.checked_sub(rhs.raw).map(TextSize) } } impl From<u32> for TextSize { + #[inline] fn from(raw: u32) -> Self { - TextSize { raw } + TextSize(raw) } } impl From<TextSize> for u32 { + #[inline] fn from(value: TextSize) -> Self { value.raw } @@ -84,19 +98,16 @@ impl From<TextSize> for u32 { 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 { - assert_lossless_conversion(); - return value.raw as usize; - - const fn assert_lossless_conversion() { - [()][(std::mem::size_of::<usize>() < std::mem::size_of::<u32>()) as usize] - } + value.raw as usize } } @@ -104,12 +115,14 @@ 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(self.raw $op other.raw) } } impl $Op<&TextSize> for TextSize { type Output = TextSize; + #[inline] fn $f(self, other: &TextSize) -> TextSize { self $op *other } @@ -119,6 +132,7 @@ macro_rules! ops { TextSize: $Op<T, Output=TextSize>, { type Output = TextSize; + #[inline] fn $f(self, other: T) -> TextSize { *self $op other } @@ -133,6 +147,7 @@ impl<A> AddAssign<A> for TextSize where TextSize: Add<A, Output = TextSize>, { + #[inline] fn add_assign(&mut self, rhs: A) { *self = *self + rhs } @@ -142,6 +157,7 @@ impl<S> SubAssign<S> for TextSize where TextSize: Sub<S, Output = TextSize>, { + #[inline] fn sub_assign(&mut self, rhs: S) { *self = *self - rhs } @@ -151,6 +167,7 @@ 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(TextSize::zero(), Add::add) } diff --git a/lib/text-size/src/traits.rs b/lib/text-size/src/traits.rs index ca4b7d9b42..8d197db8c1 100644 --- a/lib/text-size/src/traits.rs +++ b/lib/text-size/src/traits.rs @@ -7,6 +7,7 @@ pub trait TextSized: Copy { } impl TextSized for &'_ str { + #[inline] fn text_size(self) -> TextSize { self.len() .try_into() @@ -15,6 +16,7 @@ impl TextSized for &'_ str { } impl TextSized for char { + #[inline] fn text_size(self) -> TextSize { TextSize(self.len_utf8() as u32) } |