text-editing 0.2.3

A simple string with utilities for editing
Documentation
use std::ops::Range;

use super::TextLine;

impl TextLine {
    /// Returns the ordered selection range from an anchor and cursor position.
    ///
    /// Returns `None` if no anchor is set.
    ///
    /// # Arguments
    /// * `cursor` - The current text cursor position.
    /// * `anchor` - The optional selection anchor position.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// assert_eq!(TextLine::selection_range(10, Some(5)), Some(5..10));
    /// assert_eq!(TextLine::selection_range(3, Some(8)), Some(3..8));
    /// assert_eq!(TextLine::selection_range(5, None), None);
    /// ```
    pub fn selection_range(cursor: usize, anchor: Option<usize>) -> Option<Range<usize>> {
        let a = anchor?;
        Some(a.min(cursor)..a.max(cursor))
    }

    /// Returns the selected text, if a selection is active.
    ///
    /// # Arguments
    /// * `cursor` - The current text cursor position.
    /// * `anchor` - The optional selection anchor position.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// assert_eq!(line.selected_text(12, Some(7)), Some("world"));
    /// assert_eq!(line.selected_text(5, None), None);
    /// ```
    pub fn selected_text(&self, cursor: usize, anchor: Option<usize>) -> Option<&str> {
        Self::selection_range(cursor, anchor)
            .map(|range| &self.text[self.string_index(range.start)..self.string_index(range.end)])
    }

    /// Deletes the currently selected text and updates cursor and anchor.
    ///
    /// Returns `true` if text was deleted, `false` if no selection was active
    /// or the selection was empty.
    ///
    /// # Arguments
    /// * `cursor` - A mutable reference to the current text cursor position.
    /// * `anchor` - A mutable reference to the optional selection anchor.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let mut line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 12;
    /// let mut anchor = Some(7);
    /// assert!(line.delete_selection(&mut cursor, &mut anchor));
    /// assert_eq!(line.as_str(), "Hello, !");
    /// assert_eq!(cursor, 7);
    /// assert_eq!(anchor, None);
    /// ```
    pub fn delete_selection(&mut self, cursor: &mut usize, anchor: &mut Option<usize>) -> bool {
        if let Some(range) = Self::selection_range(*cursor, *anchor)
            && !range.is_empty()
        {
            self.remove_range(range.clone());
            *cursor = range.start;
            *anchor = None;
            return true;
        }
        *anchor = None;
        false
    }

    /// Replaces the current selection with the given text.
    ///
    /// If no selection is active, inserts the text at the cursor position.
    ///
    /// # Arguments
    /// * `cursor` - A mutable reference to the current text cursor position.
    /// * `anchor` - A mutable reference to the optional selection anchor.
    /// * `text` - The replacement text.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let mut line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 12;
    /// let mut anchor = Some(7);
    /// line.replace_selection(&mut cursor, &mut anchor, "Rust");
    /// assert_eq!(line.as_str(), "Hello, Rust!");
    /// assert_eq!(cursor, 11);
    /// assert_eq!(anchor, None);
    /// ```
    pub fn replace_selection(
        &mut self,
        cursor: &mut usize,
        anchor: &mut Option<usize>,
        text: &str,
    ) {
        self.delete_selection(cursor, anchor);
        self.insert_str(cursor, text);
    }

    /// Selects all text in the line.
    ///
    /// Sets the anchor to the beginning and the cursor to the end.
    ///
    /// # Arguments
    /// * `cursor` - A mutable reference to the current text cursor position.
    /// * `anchor` - A mutable reference to the optional selection anchor.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 5;
    /// let mut anchor = None;
    /// line.select_all(&mut cursor, &mut anchor);
    /// assert_eq!(cursor, 13);
    /// assert_eq!(anchor, Some(0));
    /// ```
    pub fn select_all(&self, cursor: &mut usize, anchor: &mut Option<usize>) {
        *anchor = Some(0);
        *cursor = self.len();
    }

    /// Extends the selection forward by one character.
    ///
    /// Sets the anchor to the current cursor position if no selection is active,
    /// then moves the cursor forward.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 7;
    /// let mut anchor = None;
    /// assert!(line.select_forward(&mut cursor, &mut anchor));
    /// assert_eq!(cursor, 8);
    /// assert_eq!(anchor, Some(7));
    /// ```
    pub fn select_forward(&self, cursor: &mut usize, anchor: &mut Option<usize>) -> bool {
        if anchor.is_none() {
            *anchor = Some(*cursor);
        }
        self.forward(cursor)
    }

    /// Extends the selection backward by one character.
    ///
    /// Sets the anchor to the current cursor position if no selection is active,
    /// then moves the cursor backward.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 7;
    /// let mut anchor = None;
    /// assert!(line.select_backward(&mut cursor, &mut anchor));
    /// assert_eq!(cursor, 6);
    /// assert_eq!(anchor, Some(7));
    /// ```
    pub fn select_backward(&self, cursor: &mut usize, anchor: &mut Option<usize>) -> bool {
        if anchor.is_none() {
            *anchor = Some(*cursor);
        }
        self.backward(cursor)
    }

    /// Extends the selection forward by one word.
    ///
    /// Sets the anchor to the current cursor position if no selection is active,
    /// then moves the cursor to the end of the next word.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 7;
    /// let mut anchor = None;
    /// assert!(line.select_forward_skip(&mut cursor, &mut anchor));
    /// assert_eq!(cursor, 12);
    /// assert_eq!(anchor, Some(7));
    /// ```
    pub fn select_forward_skip(&self, cursor: &mut usize, anchor: &mut Option<usize>) -> bool {
        if anchor.is_none() {
            *anchor = Some(*cursor);
        }
        self.skip_forward(cursor)
    }

    /// Extends the selection backward by one word.
    ///
    /// Sets the anchor to the current cursor position if no selection is active,
    /// then moves the cursor to the start of the previous word.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut cursor = 12;
    /// let mut anchor = None;
    /// assert!(line.select_backward_skip(&mut cursor, &mut anchor));
    /// assert_eq!(cursor, 7);
    /// assert_eq!(anchor, Some(12));
    /// ```
    pub fn select_backward_skip(&self, cursor: &mut usize, anchor: &mut Option<usize>) -> bool {
        if anchor.is_none() {
            *anchor = Some(*cursor);
        }
        self.skip_backward(cursor)
    }

    /// Clears the current selection without modifying the text.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let mut anchor = Some(5);
    /// TextLine::clear_selection(&mut anchor);
    /// assert_eq!(anchor, None);
    /// ```
    pub fn clear_selection(anchor: &mut Option<usize>) {
        *anchor = None;
    }
}