Skip to main content

redox_core/buffer/text_buffer/
slicing.rs

1//! `TextBuffer` slicing helpers.
2//!
3//! Design notes:
4//! - All indices are **character indices** (Unicode scalar values) to match `ropey`.
5//! - Non-allocating `RopeSlice` APIs are provided for hot paths.
6//! - Allocating `String` helpers remain for ergonomics.
7
8use std::cmp::min;
9
10use ropey::RopeSlice;
11
12use super::TextBuffer;
13use crate::buffer::{Pos, Selection};
14
15impl TextBuffer {
16    /// Get a non-allocating slice for a character range `[start, end)`.
17    ///
18    /// - Indices are clamped to the buffer bounds.
19    /// - If `start > end`, the values are swapped.
20    pub fn slice_chars_ref(&self, mut start: usize, mut end: usize) -> RopeSlice<'_> {
21        let maxc = self.len_chars();
22        start = min(start, maxc);
23        end = min(end, maxc);
24        if start > end {
25            std::mem::swap(&mut start, &mut end);
26        }
27        self.rope().slice(start..end)
28    }
29
30    /// Get a non-allocating slice for a selection (ordered).
31    pub fn slice_selection_ref(&self, sel: Selection) -> RopeSlice<'_> {
32        let (a, b) = sel.ordered();
33        let start = self.pos_to_char(a);
34        let end = self.pos_to_char(b);
35        self.slice_chars_ref(start, end)
36    }
37
38    /// Convenience: non-allocating slice by two logical positions (order-independent).
39    pub fn slice_pos_range_ref(&self, a: Pos, b: Pos) -> RopeSlice<'_> {
40        let start = self.pos_to_char(a);
41        let end = self.pos_to_char(b);
42        self.slice_chars_ref(start, end)
43    }
44
45    /// Get the full buffer as a `String`.
46    ///
47    /// For large buffers, this allocates. Kept as an inherent method so call sites
48    /// can use `b.to_string()` without depending on `Display`/`ToString`.
49    #[inline]
50    pub fn to_string(&self) -> String {
51        self.rope().to_string()
52    }
53
54    /// Get a `String` for a character range `[start, end)`.
55    ///
56    /// - Indices are clamped to the buffer bounds.
57    /// - If `start > end`, the values are swapped.
58    ///
59    /// This is a convenience API; it allocates.
60    pub fn slice_chars(&self, start: usize, end: usize) -> String {
61        self.slice_chars_ref(start, end).to_string()
62    }
63
64    /// Get the selected text for a selection (ordered).
65    ///
66    /// This is a convenience API; it allocates.
67    pub fn slice_selection(&self, sel: Selection) -> String {
68        self.slice_selection_ref(sel).to_string()
69    }
70
71    /// Convenience: slice by two logical positions (order-independent).
72    ///
73    /// This is useful when there are two cursors/marks and the substring
74    /// between them without explicitly building a `Selection`.
75    pub fn slice_pos_range(&self, a: Pos, b: Pos) -> String {
76        self.slice_pos_range_ref(a, b).to_string()
77    }
78}