aboutsummaryrefslogblamecommitdiffstats
path: root/src/util/slice.rs
blob: 21340693e820785dfadfda2ae6c08b8f1f3c8814 (plain) (tree)



























































































































































                                                                                            
//! Utilities to deal with characters.

use crate::constant::TAB_SIZE;
use crate::tokenizer::{Event, EventType, Point};

/// A range between two places.
#[derive(Debug)]
pub struct Position<'a> {
    pub start: &'a Point,
    pub end: &'a Point,
}

impl<'a> Position<'a> {
    /// Get a position from an exit event.
    ///
    /// Looks backwards for the corresponding `enter` event.
    /// This does not support nested events (such as lists in lists).
    ///
    /// ## Panics
    ///
    /// This function panics if an enter event is given.
    /// When `micromark` is used, this function never panics.
    pub fn from_exit_event(events: &'a [Event], index: usize) -> Position<'a> {
        let exit = &events[index];
        assert_eq!(
            exit.event_type,
            EventType::Exit,
            "expected `from_exit_event` to be called on `exit` event"
        );
        let mut enter_index = index - 1;

        loop {
            let enter = &events[enter_index];
            if enter.event_type == EventType::Enter && enter.token_type == exit.token_type {
                return Position {
                    start: &enter.point,
                    end: &exit.point,
                };
            }

            enter_index -= 1;
        }
    }
}

/// Chars belonging to a range.
///
/// Includes information on virtual spaces before and after the chars.
#[derive(Debug)]
pub struct Slice<'a> {
    pub chars: &'a [char],
    pub before: usize,
    pub after: usize,
}

impl<'a> Slice<'a> {
    /// Get the slice belonging to a position.
    pub fn from_point(list: &'a [char], point: &Point) -> Slice<'a> {
        let mut before = point.vs;
        let mut start = point.index;
        let end = if start < list.len() { start + 1 } else { start };

        // If we have virtual spaces before, it means we are past the actual
        // character at that index, and those virtual spaces.
        if before > 0 {
            before = TAB_SIZE - before;
            start += 1;
        };

        Slice {
            chars: if start < end { &list[start..end] } else { &[] },
            before,
            after: 0,
        }
    }

    /// Get the slice belonging to a position.
    pub fn from_position(list: &'a [char], position: &Position) -> Slice<'a> {
        let mut before = position.start.vs;
        let mut after = position.end.vs;
        let mut start = position.start.index;
        let mut end = position.end.index;

        // If we have virtual spaces before, it means we are past the actual
        // character at that index, and those virtual spaces.
        if before > 0 {
            before = TAB_SIZE - before;
            start += 1;
        };

        // If we have virtual spaces after, it means that character is included,
        // and one less virtual space.
        if after > 0 {
            after -= 1;
            end += 1;
        }

        Slice {
            chars: &list[start..end],
            before,
            after,
        }
    }

    /// To do.
    pub fn size(&self) -> usize {
        self.chars.len() + self.before + self.after
    }

    // To do:
    // When we have u8s, we could use: <https://doc.rust-lang.org/std/str/fn.from_utf8.html>
    // to implement an `as_str`.

    /// To do.
    pub fn head(&self) -> Option<char> {
        if self.before > 0 {
            Some(' ')
        } else if self.chars.is_empty() {
            None
        } else {
            Some(self.chars[0])
        }
    }

    /// To do.
    pub fn tail(&self) -> Option<char> {
        if self.after > 0 {
            Some(' ')
        } else {
            let index = self.chars.len();
            if index > 0 {
                Some(self.chars[index - 1])
            } else {
                None
            }
        }
    }

    /// To do.
    pub fn serialize(&self) -> String {
        let mut string = String::with_capacity(self.size());
        let mut index = self.before;
        while index > 0 {
            string.push(' ');
            index -= 1;
        }
        string.push_str(&self.chars.iter().collect::<String>());
        index = self.after;
        while index > 0 {
            string.push(' ');
            index -= 1;
        }

        string
    }
}