#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Position {
pub line: usize,
pub column: usize,
}
impl Position {
#[must_use]
pub const fn new(line: usize, column: usize) -> Self {
Self { line, column }
}
#[must_use]
pub const fn origin() -> Self {
Self { line: 0, column: 0 }
}
#[must_use]
pub const fn line_start(line: usize) -> Self {
Self { line, column: 0 }
}
}
impl PartialOrd for Position {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Position {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.line.cmp(&other.line) {
std::cmp::Ordering::Equal => self.column.cmp(&other.column),
ord => ord,
}
}
}
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.line + 1, self.column + 1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Cursor {
pub position: Position,
pub anchor: Option<Position>,
pub preferred_column: Option<usize>,
}
impl Cursor {
#[must_use]
pub const fn new(position: Position) -> Self {
Self {
position,
anchor: None,
preferred_column: None,
}
}
#[must_use]
pub const fn origin() -> Self {
Self::new(Position::origin())
}
pub const fn start_selection(&mut self) {
self.anchor = Some(self.position);
}
pub const fn clear_selection(&mut self) {
self.anchor = None;
}
#[must_use]
pub const fn has_selection(&self) -> bool {
self.anchor.is_some()
}
#[must_use]
pub fn selection_bounds(&self) -> Option<(Position, Position)> {
self.anchor.map(|anchor| {
if anchor <= self.position {
(anchor, self.position)
} else {
(self.position, anchor)
}
})
}
pub const fn update_preferred_column(&mut self) {
self.preferred_column = Some(self.position.column);
}
pub const fn clear_preferred_column(&mut self) {
self.preferred_column = None;
}
#[must_use]
pub fn effective_column(&self) -> usize {
self.preferred_column.unwrap_or(self.position.column)
}
}