From d30041236528533fbf241fe139529d58429a7227 Mon Sep 17 00:00:00 2001 From: Hana Date: Wed, 11 Dec 2024 14:43:26 +0800 Subject: [PATCH] docs: more docs --- src/helpers.rs | 37 +++++++++++++++++++++++++++---------- src/rope.rs | 27 ++++++++++++++++++++------- tests/compat_source.rs | 1 + 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 58bcb91..95742ed 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1292,17 +1292,34 @@ pub fn stream_and_get_source_and_map<'a, S: StreamChunks>( (generated_info, map) } +/// Represents a text source that can be manipulated for source mapping purposes. pub trait SourceText<'a>: Default + Clone + ToString { - fn split_into_lines(&self) -> impl Iterator; - fn len(&self) -> usize; - fn ends_with(&self, value: &str) -> bool; - fn char_indices(&self) -> impl Iterator; - fn byte_slice(&self, range: Range) -> Self; - fn is_empty(&self) -> bool; - fn into_rope(self) -> Rope<'a> - where - Self: Sized; - fn get_byte(&self, byte_index: usize) -> Option; + /// Splits the text into lines, returning an iterator over each line. + /// Each line includes its line ending character if present. + fn split_into_lines(&self) -> impl Iterator; + + /// Returns the length of the text in bytes. + fn len(&self) -> usize; + + /// Checks if the text ends with the given string. + fn ends_with(&self, value: &str) -> bool; + + /// Returns an iterator over the char indices in the text. + fn char_indices(&self) -> impl Iterator; + + /// Gets the byte at the specified index, if it exists. + fn get_byte(&self, byte_index: usize) -> Option; + + /// Returns a slice of the text specified by the byte range. + fn byte_slice(&self, range: Range) -> Self; + + /// Returns true if the text is empty. + fn is_empty(&self) -> bool; + + /// Converts this text into a Rope. + fn into_rope(self) -> Rope<'a> + where + Self: Sized; } impl<'a> SourceText<'a> for Rope<'a> { diff --git a/src/rope.rs b/src/rope.rs index a6256f5..3e3971d 100644 --- a/src/rope.rs +++ b/src/rope.rs @@ -258,7 +258,7 @@ impl<'a> Rope<'a> { Repr::Simple(s) => s .get(start_range..end_range) .map(Rope::from) - .ok_or_else(|| Error::Rope("invalid char boundary")), + .ok_or(Error::Rope("invalid char boundary")), Repr::Complex(data) => { // [start_chunk let start_chunk_index = data @@ -275,13 +275,15 @@ impl<'a> Rope<'a> { // same chunk if start_chunk_index == end_chunk_index { - let (chunk, start_pos) = data[start_chunk_index]; + // SAFETY: start_chunk_index guarantees valid range + let (chunk, start_pos) = + unsafe { data.get_unchecked(start_chunk_index) }; let start = start_range - start_pos; let end = end_range - start_pos; return chunk .get(start..end) .map(Rope::from) - .ok_or_else(|| Error::Rope("invalid char boundary")); + .ok_or(Error::Rope("invalid char boundary")); } if end_chunk_index < start_chunk_index { @@ -295,7 +297,8 @@ impl<'a> Rope<'a> { // different chunk // [start_chunk, end_chunk] (start_chunk_index..end_chunk_index + 1).try_for_each(|i| { - let (chunk, start_pos) = data[i]; + // SAFETY: [start_chunk_index, end_chunk_index] guarantees valid range + let (chunk, start_pos) = unsafe { data.get_unchecked(i) }; if start_chunk_index == i { let start = start_range - start_pos; @@ -328,8 +331,15 @@ impl<'a> Rope<'a> { } } - /// Unchecked version of [Rope::byte_slice]. - /// Invariant: The range must be valid and on char boundaries. + /// Range-unchecked version of [Rope::byte_slice]. + /// + /// # Safety + /// + /// This is not safe, due to the following invariants that must be upheld: + /// + /// - Range must be within bounds. + /// - Range start must be less than or equal to the end. + /// - Both range start and end must be on char boundaries. pub unsafe fn byte_slice_unchecked(&self, range: R) -> Rope<'a> where R: RangeBounds, @@ -425,7 +435,7 @@ impl<'a> Rope<'a> { } } - /// Returns the underlying str if this is a simple rope. + /// Returns the underlying &str if this is a simple rope. pub fn get_simple(&self) -> Option<&'a str> { match &self.repr { Repr::Simple(s) => Some(s), @@ -503,6 +513,9 @@ impl Default for Rope<'_> { } } +// Implement `ToString` than `Display` to manually allocate the string with capacity. +// This is faster than using `Display` and `write!` for large ropes. +#[allow(clippy::to_string_trait_impl)] impl ToString for Rope<'_> { fn to_string(&self) -> String { match &self.repr { diff --git a/tests/compat_source.rs b/tests/compat_source.rs index 9a618b2..1a9347d 100644 --- a/tests/compat_source.rs +++ b/tests/compat_source.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] use std::borrow::Cow; use std::hash::Hash;