use super::Position;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum SelectionMode {
#[default]
Character,
Block,
Line,
}
impl SelectionMode {
#[inline]
#[must_use]
pub const fn is_character(&self) -> bool {
matches!(self, Self::Character)
}
#[inline]
#[must_use]
pub const fn is_block(&self) -> bool {
matches!(self, Self::Block)
}
#[inline]
#[must_use]
pub const fn is_line(&self) -> bool {
matches!(self, Self::Line)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Selection {
pub anchor: Position,
pub active: bool,
pub mode: SelectionMode,
}
impl Selection {
#[must_use]
pub const fn new() -> Self {
Self {
anchor: Position::origin(),
active: false,
mode: SelectionMode::Character,
}
}
pub const fn start(&mut self, pos: Position, mode: SelectionMode) {
self.anchor = pos;
self.active = true;
self.mode = mode;
}
pub const fn start_char(&mut self, pos: Position) {
self.start(pos, SelectionMode::Character);
}
pub const fn start_line(&mut self, pos: Position) {
self.start(pos, SelectionMode::Line);
}
pub const fn start_block(&mut self, pos: Position) {
self.start(pos, SelectionMode::Block);
}
pub const fn clear(&mut self) {
self.active = false;
}
#[inline]
#[must_use]
pub const fn is_active(&self) -> bool {
self.active
}
#[inline]
#[must_use]
pub const fn mode(&self) -> SelectionMode {
self.mode
}
pub const fn set_mode(&mut self, mode: SelectionMode) {
self.mode = mode;
}
#[must_use]
pub fn bounds(&self, cursor_pos: Position) -> Option<(Position, Position)> {
if !self.active {
return None;
}
if self.anchor <= cursor_pos {
Some((self.anchor, cursor_pos))
} else {
Some((cursor_pos, self.anchor))
}
}
#[must_use]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn block_bounds(&self, cursor_pos: Position) -> Option<(Position, Position)> {
if !self.active || self.mode != SelectionMode::Block {
return None;
}
let min_line = self.anchor.line.min(cursor_pos.line);
let max_line = self.anchor.line.max(cursor_pos.line);
let min_col = self.anchor.column.min(cursor_pos.column);
let max_col = self.anchor.column.max(cursor_pos.column);
Some((Position::new(min_line, min_col), Position::new(max_line, max_col)))
}
#[must_use]
pub fn line_bounds(&self, cursor_pos: Position) -> Option<(usize, usize)> {
if !self.active {
return None;
}
let start_line = self.anchor.line.min(cursor_pos.line);
let end_line = self.anchor.line.max(cursor_pos.line);
Some((start_line, end_line))
}
#[must_use]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn contains(&self, pos: Position, cursor_pos: Position) -> bool {
if !self.active {
return false;
}
match self.mode {
SelectionMode::Character => {
if let Some((start, end)) = self.bounds(cursor_pos) {
pos >= start && pos <= end
} else {
false
}
}
SelectionMode::Block => {
if let Some((top_left, bottom_right)) = self.block_bounds(cursor_pos) {
pos.line >= top_left.line
&& pos.line <= bottom_right.line
&& pos.column >= top_left.column
&& pos.column <= bottom_right.column
} else {
false
}
}
SelectionMode::Line => {
if let Some((start_line, end_line)) = self.line_bounds(cursor_pos) {
pos.line >= start_line && pos.line <= end_line
} else {
false
}
}
}
}
#[must_use]
pub fn line_count(&self, cursor_pos: Position) -> usize {
if !self.active {
return 0;
}
let start_line = self.anchor.line.min(cursor_pos.line);
let end_line = self.anchor.line.max(cursor_pos.line);
end_line - start_line + 1
}
}