aboutsummaryrefslogtreecommitdiffstats
path: root/src/util/slice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/slice.rs')
-rw-r--r--src/util/slice.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/util/slice.rs b/src/util/slice.rs
new file mode 100644
index 0000000..2134069
--- /dev/null
+++ b/src/util/slice.rs
@@ -0,0 +1,156 @@
+//! 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
+ }
+}