use hjkl_vim_types::{Mode, Operator, RangeKind};
use super::*;
use crate::vim_state::vim_mut;
use hjkl_engine::Editor;
use hjkl_engine::buf_helpers::{buf_line, buf_line_chars, buf_row_count, buf_set_cursor_rc};
pub fn expand_linewise_over_closed_folds(
buf: &hjkl_buffer::View,
mut start: usize,
mut end: usize,
) -> (usize, usize) {
let folds = buf.folds();
if folds.is_empty() {
return (start, end);
}
loop {
let mut changed = false;
for f in &folds {
if !f.closed {
continue;
}
if f.start_row <= end && f.end_row >= start {
if f.start_row < start {
start = f.start_row;
changed = true;
}
if f.end_row > end {
end = f.end_row;
changed = true;
}
}
}
if !changed {
break;
}
}
(start, end)
}
pub fn land_after_linewise_delete<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
top: usize,
deleted_through_last: bool,
) {
let total_after = buf_row_count(ed.buffer());
let raw_target = if deleted_through_last {
top.saturating_sub(1).min(total_after.saturating_sub(1))
} else {
top.min(total_after.saturating_sub(1))
};
let target_row = if raw_target > 0
&& raw_target + 1 == total_after
&& buf_line(ed.buffer(), raw_target).is_some_and(|s| s.is_empty())
{
raw_target - 1
} else {
raw_target
};
buf_set_cursor_rc(ed.buffer_mut(), target_row, 0);
move_first_non_whitespace(ed);
}
pub fn execute_line_op<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
op: Operator,
count: usize,
) {
if count == 1
&& !matches!(
op,
Operator::Yank | Operator::Reflow | Operator::ReflowKeepCursor
)
{
let (row, col) = ed.cursor();
move_first_non_whitespace(ed);
if ed.cursor().1 > col {
buf_set_cursor_rc(ed.buffer_mut(), row, col);
}
}
execute_line_op_from_cursor(ed, op, count, 1);
}
pub fn execute_line_op_from_cursor<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
op: Operator,
count: usize,
levels: usize,
) {
line_op_body(ed, op, count, levels);
remember_operator_column(ed);
}
fn line_op_body<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
op: Operator,
count: usize,
levels: usize,
) {
let count = count.min(MAX_COUNT);
let (row, col) = ed.cursor();
let total = buf_row_count(ed.buffer());
if count >= 2 && row >= last_content_row(ed) {
return;
}
let end_row = (row + count.saturating_sub(1)).min(total.saturating_sub(1));
let (row, end_row) = expand_linewise_over_closed_folds(ed.buffer(), row, end_row);
if matches!(op, Operator::Delete | Operator::Yank | Operator::Change)
&& refuse_read_only_register(ed)
{
buf_set_cursor_rc(ed.buffer_mut(), row, col);
if op != Operator::Yank {
move_first_non_whitespace(ed);
}
let at = ed.cursor();
refused_operator(ed, op, at, RangeKind::Linewise);
return;
}
match op {
Operator::Yank => {
let text = read_vim_range(ed, (row, col), (end_row, 0), RangeKind::Linewise);
if !text.is_empty() {
ed.record_yank_to_host(text.clone());
let target = vim_mut(ed).pending_register.take();
ed.record_yank(text, true, target);
}
let last_col = buf_line_chars(ed.buffer(), end_row).saturating_sub(1);
ed.set_mark('[', (row, 0));
ed.set_mark(']', (end_row, last_col));
buf_set_cursor_rc(ed.buffer_mut(), row, col);
vim_mut(ed).mode = Mode::Normal;
}
Operator::Delete => {
ed.push_undo();
let deleted_through_last = end_row + 1 >= total;
cut_vim_range(ed, (row, col), (end_row, 0), RangeKind::Linewise);
land_after_linewise_delete(ed, row, deleted_through_last);
remember_cursor_column(ed);
vim_mut(ed).mode = Mode::Normal;
let pos = ed.cursor();
ed.set_mark('[', pos);
ed.set_mark(']', pos);
}
Operator::Change => {
change_linewise_rows(ed, row, end_row, col);
}
Operator::Uppercase | Operator::Lowercase | Operator::ToggleCase | Operator::Rot13 => {
apply_case_op_to_selection(ed, op, (row, col), (end_row, 0), RangeKind::Linewise);
move_first_non_whitespace(ed);
}
Operator::Indent | Operator::Outdent => {
ed.push_undo();
if op == Operator::Indent {
indent_rows(ed, row, end_row, levels);
} else {
outdent_rows(ed, row, end_row, levels);
}
remember_cursor_column(ed);
vim_mut(ed).mode = Mode::Normal;
}
Operator::Fold => unreachable!("Fold has no line-op double"),
Operator::Reflow => {
ed.push_undo();
reflow_rows(ed, row, end_row);
move_first_non_whitespace(ed);
remember_cursor_column(ed);
vim_mut(ed).mode = Mode::Normal;
}
Operator::ReflowKeepCursor => {
let saved = ed.cursor();
ed.push_undo();
let (before, after, opts) = reflow_rows_keep_cursor(ed, row, end_row);
let (new_row, new_col) =
reflow_keep_cursor(row, saved.0, saved.1, &before, &after, opts);
buf_set_cursor_rc(ed.buffer_mut(), new_row, new_col);
remember_cursor_column(ed);
vim_mut(ed).mode = Mode::Normal;
}
Operator::AutoIndent => {
ed.push_undo();
auto_indent_rows(ed, row, end_row);
remember_cursor_column(ed);
vim_mut(ed).mode = Mode::Normal;
}
Operator::Filter => {
}
Operator::Comment => {
}
}
}