movement.rs

  1use super::{Bias, DisplayMapSnapshot, DisplayPoint, SelectionGoal};
  2use anyhow::Result;
  3
  4pub fn left(map: &DisplayMapSnapshot, mut point: DisplayPoint) -> Result<DisplayPoint> {
  5    if point.column() > 0 {
  6        *point.column_mut() -= 1;
  7    } else if point.row() > 0 {
  8        *point.row_mut() -= 1;
  9        *point.column_mut() = map.line_len(point.row());
 10    }
 11    Ok(map.clip_point(point, Bias::Left))
 12}
 13
 14pub fn right(map: &DisplayMapSnapshot, mut point: DisplayPoint) -> Result<DisplayPoint> {
 15    let max_column = map.line_len(point.row());
 16    if point.column() < max_column {
 17        *point.column_mut() += 1;
 18    } else if point.row() < map.max_point().row() {
 19        *point.row_mut() += 1;
 20        *point.column_mut() = 0;
 21    }
 22    Ok(map.clip_point(point, Bias::Right))
 23}
 24
 25pub fn up(
 26    map: &DisplayMapSnapshot,
 27    mut point: DisplayPoint,
 28    goal: SelectionGoal,
 29) -> Result<(DisplayPoint, SelectionGoal)> {
 30    let goal_column = if let SelectionGoal::Column(column) = goal {
 31        column
 32    } else {
 33        map.column_to_chars(point.row(), point.column())
 34    };
 35
 36    if point.row() > 0 {
 37        *point.row_mut() -= 1;
 38        *point.column_mut() = map.column_from_chars(point.row(), goal_column);
 39    } else {
 40        point = DisplayPoint::new(0, 0);
 41    }
 42
 43    Ok((point, SelectionGoal::Column(goal_column)))
 44}
 45
 46pub fn down(
 47    map: &DisplayMapSnapshot,
 48    mut point: DisplayPoint,
 49    goal: SelectionGoal,
 50) -> Result<(DisplayPoint, SelectionGoal)> {
 51    let max_point = map.max_point();
 52    let goal_column = if let SelectionGoal::Column(column) = goal {
 53        column
 54    } else {
 55        map.column_to_chars(point.row(), point.column())
 56    };
 57
 58    if point.row() < max_point.row() {
 59        *point.row_mut() += 1;
 60        *point.column_mut() = map.column_from_chars(point.row(), goal_column);
 61    } else {
 62        point = max_point;
 63    }
 64
 65    Ok((point, SelectionGoal::Column(goal_column)))
 66}
 67
 68pub fn line_beginning(
 69    map: &DisplayMapSnapshot,
 70    point: DisplayPoint,
 71    toggle_indent: bool,
 72) -> Result<DisplayPoint> {
 73    let (indent, is_blank) = map.line_indent(point.row());
 74    if toggle_indent && !is_blank && point.column() != indent {
 75        Ok(DisplayPoint::new(point.row(), indent))
 76    } else {
 77        Ok(DisplayPoint::new(point.row(), 0))
 78    }
 79}
 80
 81pub fn line_end(map: &DisplayMapSnapshot, point: DisplayPoint) -> Result<DisplayPoint> {
 82    Ok(DisplayPoint::new(point.row(), map.line_len(point.row())))
 83}
 84
 85pub fn prev_word_boundary(map: &DisplayMapSnapshot, point: DisplayPoint) -> Result<DisplayPoint> {
 86    if point.column() == 0 {
 87        if point.row() == 0 {
 88            Ok(DisplayPoint::new(0, 0))
 89        } else {
 90            let row = point.row() - 1;
 91            Ok(DisplayPoint::new(row, map.line_len(row)))
 92        }
 93    } else {
 94        let mut boundary = DisplayPoint::new(point.row(), 0);
 95        let mut column = 0;
 96        let mut prev_c = None;
 97        for c in map.chars_at(boundary) {
 98            if column >= point.column() {
 99                break;
100            }
101
102            if prev_c.is_none() || char_kind(prev_c.unwrap()) != char_kind(c) {
103                *boundary.column_mut() = column;
104            }
105
106            prev_c = Some(c);
107            column += c.len_utf8() as u32;
108        }
109        Ok(boundary)
110    }
111}
112
113pub fn next_word_boundary(
114    map: &DisplayMapSnapshot,
115    mut point: DisplayPoint,
116) -> Result<DisplayPoint> {
117    let mut prev_c = None;
118    for c in map.chars_at(point) {
119        if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
120            break;
121        }
122
123        if c == '\n' {
124            *point.row_mut() += 1;
125            *point.column_mut() = 0;
126        } else {
127            *point.column_mut() += c.len_utf8() as u32;
128        }
129        prev_c = Some(c);
130    }
131    Ok(point)
132}
133
134#[derive(Copy, Clone, Eq, PartialEq)]
135enum CharKind {
136    Newline,
137    Whitespace,
138    Punctuation,
139    Word,
140}
141
142fn char_kind(c: char) -> CharKind {
143    if c == '\n' {
144        CharKind::Newline
145    } else if c.is_whitespace() {
146        CharKind::Whitespace
147    } else if c.is_alphanumeric() || c == '_' {
148        CharKind::Word
149    } else {
150        CharKind::Punctuation
151    }
152}