text-editing 0.2.2

A simple string with utilities for editing
Documentation
use super::TextLine;

#[derive(Copy, Clone, PartialEq, Eq)]
enum CharKind {
    Whitespace,
    Alphanumeric,
    Other,
}

impl CharKind {
    fn of(c: char) -> Self {
        use CharKind::*;
        if c.is_whitespace() {
            Whitespace
        } else if c.is_alphanumeric() {
            Alphanumeric
        } else {
            Other
        }
    }
}

/// Represents the direction of movement for the text cursor.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Direction {
    /// Moves the text cursor forward.
    Forward,
    /// Moves the text cursor backward.
    Backward,
}

impl TextLine {
    /// Returns the appropriate movement function based on parameters `movement` and `skip`.
    ///
    /// # Arguments
    /// * `movement` - The direction of movement (`Forward` or `Backward`).
    /// * `skip` - Whether to skip over whitespace characters.
    ///
    /// # Returns
    /// A function pointer to the appropriate movement function.
    ///
    /// # Examples
    /// ```
    /// use text_editing::{TextLine, Direction};
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut text_cursor = 7;
    /// let movement_fn = TextLine::cursor_movement(Direction::Forward, false);
    /// movement_fn(&line, &mut text_cursor);
    /// assert_eq!(text_cursor, 8);
    /// ```
    pub fn cursor_movement(movement: Direction, skip: bool) -> fn(&Self, &mut usize) -> bool {
        use Direction::*;
        match (movement, skip) {
            (Backward, false) => Self::backward,
            (Backward, true) => Self::skip_backward,
            (Forward, false) => Self::forward,
            (Forward, true) => Self::skip_forward,
        }
    }

    /// Moves the text cursor forward by one.
    ///
    /// # Arguments
    /// * `text_cursor` - A mutable reference to the current text cursor position.
    ///
    /// # Returns
    /// `true` if the cursor was successfully moved forward, `false` otherwise.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut text_cursor = 6;
    /// assert!(line.forward(&mut text_cursor));
    /// assert_eq!(text_cursor, 7);
    /// ```
    pub fn forward(&self, text_cursor: &mut usize) -> bool {
        if *text_cursor < self.len() {
            *text_cursor += 1;
            true
        } else {
            false
        }
    }

    /// Moves the text cursor backward by one.
    ///
    /// # Arguments
    /// * `text_cursor` - A mutable reference to the current text cursor position.
    ///
    /// # Returns
    /// `true` if the cursor was successfully moved backward, `false` otherwise.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut text_cursor = 7;
    /// assert!(line.backward(&mut text_cursor));
    /// assert_eq!(text_cursor, 6);
    /// ```
    pub fn backward(&self, text_cursor: &mut usize) -> bool {
        if *text_cursor > 0 {
            *text_cursor -= 1;
            true
        } else {
            false
        }
    }

    /// Moves the text cursor forward until the end of the current word.
    ///
    /// # Arguments
    /// * `text_cursor` - A mutable reference to the current text cursor position.
    ///
    /// # Returns
    /// `true` if the cursor was successfully moved to the end of the word, `false` otherwise.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut text_cursor = 6;
    /// assert!(line.skip_forward(&mut text_cursor));
    /// assert_eq!(text_cursor, 12);
    /// ```
    pub fn skip_forward(&self, text_cursor: &mut usize) -> bool {
        let len = self.len();

        let start_kind = loop {
            if *text_cursor == len {
                return false;
            }

            match CharKind::of(self.char_at(*text_cursor)) {
                CharKind::Whitespace => (),
                kind => break kind,
            }

            *text_cursor += 1;
        };

        loop {
            *text_cursor += 1;

            if *text_cursor == len {
                return true;
            }

            if CharKind::of(self.char_at(*text_cursor)) != start_kind {
                return true;
            }
        }
    }

    /// Moves the text cursor back until the start of the current word.
    ///
    /// # Arguments
    /// * `text_cursor` - A mutable reference to the current text cursor position.
    ///
    /// # Returns
    /// `true` if the cursor was successfully moved to the start of the word, `false` otherwise.
    ///
    /// # Examples
    /// ```
    /// use text_editing::TextLine;
    ///
    /// let line = TextLine::from_string("Hello, world!".into());
    /// let mut text_cursor = 10;
    /// assert!(line.skip_backward(&mut text_cursor));
    /// assert_eq!(text_cursor, 7);
    /// ```
    pub fn skip_backward(&self, text_cursor: &mut usize) -> bool {
        if *text_cursor == 0 {
            return false;
        }

        let start_kind = loop {
            *text_cursor -= 1;

            if *text_cursor == 0 {
                return false;
            }

            match CharKind::of(self.char_at(*text_cursor)) {
                CharKind::Whitespace => (),
                kind => break kind,
            }
        };

        loop {
            *text_cursor -= 1;

            if CharKind::of(self.char_at(*text_cursor)) != start_kind {
                *text_cursor += 1;
                return true;
            }

            if *text_cursor == 0 {
                return true;
            }
        }
    }
}