use super::{Editor, Mode};
impl Editor {
pub(crate) fn scroll_half_page(&mut self, down: bool) {
self.scroll_by(self.view_rows().max(2) / 2, down);
}
pub(crate) fn scroll_full_page(&mut self, down: bool) {
self.scroll_by(self.view_rows().saturating_sub(1).max(1), down);
}
pub(crate) fn scroll_by(&mut self, rows: usize, down: bool) {
let line = self.buf().line_of(self.head());
let last = self.buf().last_content_line();
let line = if down {
(line + rows).min(last)
} else {
line.saturating_sub(rows)
};
let col = self.buf().col_of(self.head());
let start = self.buf().line_start(line);
let end = self.buf().line_end(line);
self.set_head((start + col).min(end));
self.clamp_cursor();
self.scroll_to_cursor(self.view_rows());
}
pub(crate) fn view_place(&mut self, place: char) {
let line = self.buf().line_of(self.head());
let rows = self.view_rows();
let last = self.buf().last_content_line();
let top = match place {
't' => line,
'b' => (line + 1).saturating_sub(rows),
_ => (line + 1).saturating_sub(rows.max(2) / 2 + 1),
};
let max_top = (last + 1).saturating_sub(rows);
self.view_mut().view_top = top.min(max_top);
}
pub(crate) fn jump_visible(&mut self, which: char, count: usize) {
let rows = self.view_rows();
let last = self.buf().last_content_line();
let line = match which {
'H' => self.view_top() + count.saturating_sub(1),
'L' => {
(self.view_top() + rows.saturating_sub(1)).saturating_sub(count.saturating_sub(1))
}
_ => self.view_top() + rows.max(2) / 2,
}
.min(last);
let s = self.buf().line_start(line);
let e = self.buf().line_end(line);
let mut p = s;
while p < e
&& self
.buf()
.byte_at(p)
.is_some_and(|b| b == b' ' || b == b'\t')
{
p += 1;
}
self.set_head(p.min(e));
self.clamp_cursor();
}
pub(crate) fn write_quit(&mut self) {
if self.buf().dirty {
if let Err(e) = self.buf_mut().save(false) {
self.message = format!("write failed: {e}");
return;
}
crate::session::save(self);
}
self.close_pane_or_buffer(false);
}
pub(crate) fn alternate_buffer(&mut self) {
let cur = self.current();
let Some(&alt) = self.mru.iter().find(|&&id| id != cur) else {
self.message = "no alternate buffer".into();
return;
};
self.switch_to(alt);
self.clamp_cursor();
}
pub(crate) fn reselect_visual(&mut self) {
let Some((anchor, head)) = self.last_visual else {
self.message = "no previous visual selection".into();
return;
};
let max = self.buf().len_bytes();
let (a, h) = (anchor.min(max), head.min(max));
let a = self.buf().clamp_boundary(a);
let h = self.buf().clamp_boundary(h);
self.sels_mut().stretch_primary(a, h);
self.mode = Mode::Visual;
}
pub(crate) fn insert_at_last(&mut self) {
let Some(pos) = self.last_insert_pos else {
self.message = "no previous insert".into();
return;
};
let pos = self.buf().clamp_boundary(pos.min(self.buf().len_bytes()));
self.set_head(pos);
self.mode = Mode::Insert;
}
pub(crate) fn change_jump(&mut self, back: bool) {
let positions = self.change_positions();
if positions.is_empty() {
self.message = "change list is empty".into();
return;
}
let depth = self.buf().history.depth();
let idx = match self.change_idx.filter(|(_, d)| *d == depth) {
None => {
if back {
0
} else {
self.message = "at the newest change".into();
return;
}
}
Some((i, _)) => {
if back {
(i + 1).min(positions.len() - 1)
} else {
i.saturating_sub(1)
}
}
};
self.change_idx = Some((idx, depth));
let pos = positions[idx];
let pos = self.buf().clamp_boundary(pos.min(self.buf().len_bytes()));
self.set_head(pos);
self.clamp_cursor();
self.scroll_to_cursor(self.view_rows());
}
fn change_positions(&self) -> Vec<usize> {
self.buf().history.change_positions()
}
}