use std::time::Instant;
use strop_core::Range;
use super::{Editor, Mode, FLASH_FOR};
impl Editor {
#[inline]
pub fn head(&self) -> usize {
self.sels().primary().head
}
#[inline]
pub fn set_head(&mut self, pos: usize) {
self.sels_mut().set_head(pos);
}
#[inline]
pub fn anchor(&self) -> usize {
self.sels().primary().anchor
}
pub fn extra_selections(&self) -> &[strop_core::selection::Selection] {
self.sels().extra_heads()
}
pub(crate) fn flash(&mut self, range: Range) {
self.flash = Some((range, Instant::now()));
}
pub fn flash_range(&self) -> Option<Range> {
self.flash
.and_then(|(r, at)| (at.elapsed() < FLASH_FOR).then_some(r))
}
pub fn clamp_cursor(&mut self) {
let line = self.buf().line_of(self.head());
let start = self.buf().line_start(line);
let end = self.buf().line_end(line);
let max = if self.mode == Mode::Insert {
end
} else {
end.max(start + 1) - 1
};
let pos = self
.buf()
.clamp_boundary(self.head().clamp(start, max.max(start)));
self.set_head(pos);
}
pub(crate) fn clamp_pos(&self, pos: usize) -> usize {
let line = self.buf().line_of(pos);
let start = self.buf().line_start(line);
let end = self.buf().line_end(line);
let max = if self.mode == Mode::Insert {
end
} else {
end.max(start + 1) - 1
};
self.buf().clamp_boundary(pos.clamp(start, max.max(start)))
}
pub(crate) fn all_cursors(&self) -> Vec<usize> {
self.sels().heads()
}
pub(crate) fn normalize_cursors(&mut self) {
self.sels_mut().normalize();
}
pub(crate) fn remap_after_mirrored_edit(&mut self, positions: &[usize], delta: isize) {
let map = |old: usize| -> usize {
let below = positions.partition_point(|&p| p < old);
let own = usize::from(positions.contains(&old));
(old as isize + delta * (below + own) as isize).max(0) as usize
};
self.set_head(map(self.head()));
let extras: Vec<usize> = self
.extra_selections()
.iter()
.map(|s| map(s.head))
.collect();
self.sels_mut().set_extras(extras);
}
pub(crate) fn toggle_cursor(&mut self) {
if self.buf().readonly {
self.message = "readonly buffer".into();
return;
}
self.sels_mut().toggle_extra();
let n = self.sels().count();
self.message = format!("{n} cursor{}", if n > 1 { "s" } else { "" });
}
pub(crate) fn add_cursor_next_line(&mut self) {
if self.buf().readonly {
self.message = "readonly buffer".into();
return;
}
let base = self
.extra_selections()
.last()
.map(|s| s.head)
.unwrap_or_else(|| self.head());
let line = self.buf().line_of(base);
if line + 1 >= self.buf().len_lines()
|| self.buf().line_start(line + 1) >= self.buf().len_bytes()
{
self.message = "no line below".into();
return;
}
let col = self.buf().col_of(base);
let start = self.buf().line_start(line + 1);
let end = self.buf().line_end(line + 1);
let pos = (start + col).min(end.saturating_sub(1).max(start));
self.sels_mut().plant_extra(pos);
let n = self.sels().count();
self.message = format!("{n} cursors");
}
pub(crate) fn collapse_cursors(&mut self) {
if self.sels().count() > 1 {
self.sels_mut().collapse_extras();
self.message = "1 cursor".into();
}
}
pub fn scroll_to_cursor(&mut self, rows: usize) {
let line = self.buf().line_of(self.head());
if line < self.view_top() {
self.view_mut().view_top = line;
} else if line >= self.view_top() + rows {
self.view_mut().view_top = line + 1 - rows;
}
}
}