Improve `Range` documentation and organization.

pull/376/head
Nathan Vegdahl 3 years ago
parent f62ec6e51e
commit b2c76dc627

@ -1,5 +1,5 @@
//! Selections are the primary editing construct. Even a single cursor is //! Selections are the primary editing construct. Even cursors are
//! defined as a single empty or 1-wide selection range. //! defined as a selection range.
//! //!
//! All positioning is done via `char` offsets into the buffer. //! All positioning is done via `char` offsets into the buffer.
use crate::{ use crate::{
@ -14,17 +14,20 @@ use std::borrow::Cow;
/// A single selection range. /// A single selection range.
/// ///
/// The range consists of an "anchor" and "head" position in /// A range consists of an "anchor" and "head" position in
/// the text. The head is the part that the user moves when /// the text. The head is the part that the user moves when
/// directly extending the selection. The head and anchor /// directly extending a selection. The head and anchor
/// can be in any order: either can precede or follow the /// can be in any order, or even share the same position.
/// other in the text, and they can share the same position ///
/// for a zero-width range. /// The anchor and head positions use gap indexing, meaning
/// that their indices represent the the gaps *between* `char`s
/// rather than the `char`s themselves. For example, 1
/// represents the position between the first and second `char`.
/// ///
/// Below are some example `Range` configurations to better /// Below are some example `Range` configurations to better
/// illustrate. The anchor and head indices are show as /// illustrate. The anchor and head indices are show as
/// "(anchor, head)", followed by example text with "[" and "]" /// "(anchor, head)", followed by example text with "[" and "]"
/// inserted to visually represent the anchor and head positions: /// inserted to represent the anchor and head positions:
/// ///
/// - (0, 3): [Som]e text. /// - (0, 3): [Som]e text.
/// - (3, 0): ]Som[e text. /// - (3, 0): ]Som[e text.
@ -37,6 +40,12 @@ use std::borrow::Cow;
/// are directly adjecent, sharing an edge, do not overlap. /// are directly adjecent, sharing an edge, do not overlap.
/// However, a zero-width range will overlap with the shared /// However, a zero-width range will overlap with the shared
/// left-edge of another range. /// left-edge of another range.
///
/// By convention, user-facing ranges are considered to have
/// a block cursor on the head-side of the range that spans a
/// single grapheme inward from the range's edge. There are a
/// variety of helper methods on `Range` for working in terms of
/// that block cursor, all of which have `cursor` in their name.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Range { pub struct Range {
/// The anchor of the range: the side that doesn't move when extending. /// The anchor of the range: the side that doesn't move when extending.
@ -73,13 +82,6 @@ impl Range {
std::cmp::max(self.anchor, self.head) std::cmp::max(self.anchor, self.head)
} }
/// The line number that the block-cursor is on.
#[inline]
#[must_use]
pub fn cursor_line(&self, text: RopeSlice) -> usize {
text.char_to_line(self.cursor(text))
}
/// The (inclusive) range of lines that the range overlaps. /// The (inclusive) range of lines that the range overlaps.
#[inline] #[inline]
#[must_use] #[must_use]
@ -184,32 +186,16 @@ impl Range {
} }
} }
/// Compute a possibly new range from this range, attempting to ensure // groupAt
/// a minimum range width of 1 char by shifting the head in the forward
/// direction as needed.
///
/// This method will never shift the anchor, and will only shift the
/// head in the forward direction. Therefore, this method can fail
/// at ensuring the minimum width if and only if the passed range is
/// both zero-width and at the end of the `RopeSlice`.
///
/// If the input range is grapheme-boundary aligned, the returned range
/// will also be. Specifically, if the head needs to shift to achieve
/// the minimum width, it will shift to the next grapheme boundary.
#[must_use]
#[inline] #[inline]
pub fn min_width_1(&self, slice: RopeSlice) -> Self { pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> {
if self.anchor == self.head { text.slice(self.from()..self.to()).into()
Range {
anchor: self.anchor,
head: next_grapheme_boundary(slice, self.head),
horiz: self.horiz,
}
} else {
*self
}
} }
//--------------------------------
// Alignment methods.
/// Compute a possibly new range from this range, with its ends /// Compute a possibly new range from this range, with its ends
/// shifted as needed to align with grapheme boundaries. /// shifted as needed to align with grapheme boundaries.
/// ///
@ -243,6 +229,35 @@ impl Range {
} }
} }
/// Compute a possibly new range from this range, attempting to ensure
/// a minimum range width of 1 char by shifting the head in the forward
/// direction as needed.
///
/// This method will never shift the anchor, and will only shift the
/// head in the forward direction. Therefore, this method can fail
/// at ensuring the minimum width if and only if the passed range is
/// both zero-width and at the end of the `RopeSlice`.
///
/// If the input range is grapheme-boundary aligned, the returned range
/// will also be. Specifically, if the head needs to shift to achieve
/// the minimum width, it will shift to the next grapheme boundary.
#[must_use]
#[inline]
pub fn min_width_1(&self, slice: RopeSlice) -> Self {
if self.anchor == self.head {
Range {
anchor: self.anchor,
head: next_grapheme_boundary(slice, self.head),
horiz: self.horiz,
}
} else {
*self
}
}
//--------------------------------
// Block-cursor methods.
/// Gets the left-side position of the block cursor. /// Gets the left-side position of the block cursor.
#[must_use] #[must_use]
#[inline] #[inline]
@ -284,11 +299,11 @@ impl Range {
} }
} }
// groupAt /// The line number that the block-cursor is on.
#[inline] #[inline]
pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> { #[must_use]
text.slice(self.from()..self.to()).into() pub fn cursor_line(&self, text: RopeSlice) -> usize {
text.char_to_line(self.cursor(text))
} }
} }

Loading…
Cancel
Save