use hjkl_vim_types::{InsertEntry, InsertReason, LastChange, Motion, Operator, TextObject};
use super::*;
use crate::vim_state::{vim, vim_mut};
use hjkl_engine::Editor;
use hjkl_engine::buf_helpers::{
buf_cursor_pos, buf_line, buf_line_chars, buf_row_count, buf_set_cursor_rc,
};
pub fn enter_insert_i_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
begin_insert(ed, count.max(1), InsertReason::Enter(InsertEntry::I));
}
pub fn enter_insert_shift_i_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
move_first_non_whitespace(ed);
begin_insert(ed, count.max(1), InsertReason::Enter(InsertEntry::ShiftI));
}
pub fn enter_insert_a_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
hjkl_engine::motions::move_right_to_end(ed.buffer_mut(), 1);
begin_insert(ed, count.max(1), InsertReason::Enter(InsertEntry::A));
}
pub fn enter_insert_shift_a_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
hjkl_engine::motions::move_line_end(ed.buffer_mut());
hjkl_engine::motions::move_right_to_end(ed.buffer_mut(), 1);
begin_insert(ed, count.max(1), InsertReason::Enter(InsertEntry::ShiftA));
}
pub fn open_line_below_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
use hjkl_buffer::{Edit, Position};
ed.push_undo();
begin_insert_noundo(ed, count.max(1), InsertReason::Open { above: false });
ed.sync_buffer_content_from_textarea();
let row = buf_cursor_pos(ed.buffer()).row;
let line_chars = buf_line_chars(ed.buffer(), row);
let prev_line = buf_line(ed.buffer(), row).unwrap_or_default();
let comment_cont = if ed.settings().formatoptions.contains('o') {
continue_comment(ed.buffer(), ed.settings(), row)
} else {
None
};
let suffix = if let Some(cont) = comment_cont {
format!("\n{cont}")
} else {
let indent = compute_enter_indent(ed.settings(), &prev_line);
format!("\n{indent}")
};
ed.mutate_edit(Edit::InsertStr {
at: Position::new(row, line_chars),
text: suffix,
});
}
pub fn open_line_above_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
use hjkl_buffer::{Edit, Position};
ed.push_undo();
begin_insert_noundo(ed, count.max(1), InsertReason::Open { above: true });
ed.sync_buffer_content_from_textarea();
let row = buf_cursor_pos(ed.buffer()).row;
let comment_cont = if ed.settings().formatoptions.contains('o') {
continue_comment(ed.buffer(), ed.settings(), row)
} else {
None
};
let (insert_text, new_line_content) = if let Some(cont) = comment_cont {
let content = cont.clone();
(format!("{cont}\n"), content)
} else {
let cur = buf_line(ed.buffer(), row).unwrap_or_default();
let indent = compute_enter_indent(ed.settings(), &cur);
let content = indent.clone();
(format!("{indent}\n"), content)
};
ed.mutate_edit(Edit::InsertStr {
at: Position::new(row, 0),
text: insert_text,
});
let folds = hjkl_engine::SnapshotFoldProvider::from_buffer(ed.buffer());
let mut sticky = ed.sticky_col();
let tabstop = ed.settings().tabstop;
hjkl_engine::motions::move_up(ed.buffer_mut(), &folds, 1, &mut sticky, tabstop);
ed.set_sticky_col(sticky);
let new_row = buf_cursor_pos(ed.buffer()).row;
buf_set_cursor_rc(ed.buffer_mut(), new_row, new_line_content.chars().count());
}
pub fn enter_replace_mode_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
begin_insert(ed, count.max(1), InsertReason::Replace);
}
pub fn delete_char_forward_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
do_char_delete(ed, true, count.max(1));
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::CharDel {
forward: true,
count: count.max(1),
});
}
}
pub fn delete_char_backward_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
do_char_delete(ed, false, count.max(1));
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::CharDel {
forward: false,
count: count.max(1),
});
}
}
pub fn substitute_char_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
use hjkl_buffer::{Edit, MotionKind, Position};
ed.push_undo();
ed.sync_buffer_content_from_textarea();
for _ in 0..count.max(1) {
let cursor = buf_cursor_pos(ed.buffer());
let line_chars = buf_line_chars(ed.buffer(), cursor.row);
if cursor.col >= line_chars {
break;
}
ed.mutate_edit(Edit::DeleteRange {
start: cursor,
end: Position::new(cursor.row, cursor.col + 1),
kind: MotionKind::Char,
});
}
begin_insert_noundo(ed, 1, InsertReason::AfterChange);
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::OpMotion {
op: Operator::Change,
motion: Motion::Right,
count: count.max(1),
inserted: None,
});
}
}
pub fn substitute_line_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
let register = vim(ed).pending_register;
execute_line_op(ed, Operator::Change, count.max(1));
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::LineOp {
op: Operator::Change,
count: count.max(1),
inserted: None,
register,
});
}
}
pub fn delete_to_eol_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
if count <= 1 {
ed.push_undo();
delete_to_eol(ed);
hjkl_engine::motions::move_left(ed.buffer_mut(), 1);
} else {
apply_op_with_motion(ed, Operator::Delete, &Motion::LineEnd, count);
}
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::DeleteToEol { inserted: None });
}
}
pub fn change_to_eol_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
let count = count.max(1);
if count <= 1 {
ed.push_undo();
delete_to_eol(ed);
begin_insert_noundo(ed, 1, InsertReason::DeleteToEol);
} else {
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::OpMotion {
op: Operator::Change,
motion: Motion::LineEnd,
count,
inserted: None,
});
}
apply_op_with_motion(ed, Operator::Change, &Motion::LineEnd, count);
}
}
pub fn yank_to_eol_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
apply_op_with_motion(ed, Operator::Yank, &Motion::LineEnd, count.max(1));
}
pub fn join_line_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
let joins = count.max(2) - 1;
for _ in 0..joins {
ed.push_undo();
if !join_line(ed) {
break;
}
}
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::JoinLine { count: joins });
}
}
pub fn toggle_case_at_cursor_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
for _ in 0..count.max(1) {
ed.push_undo();
if !toggle_case_at_cursor(ed) {
break;
}
}
let cursor = buf_cursor_pos(ed.buffer());
let line_chars = buf_line_chars(ed.buffer(), cursor.row);
if line_chars > 0 && cursor.col >= line_chars {
buf_set_cursor_rc(ed.buffer_mut(), cursor.row, line_chars - 1);
}
if !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::ToggleCase {
count: count.max(1),
});
}
}
pub fn paste_after_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
paste_bridge(ed, false, count, false, false);
}
pub fn paste_before_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
paste_bridge(ed, true, count, false, false);
}
pub fn paste_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
before: bool,
count: usize,
cursor_after: bool,
reindent: bool,
) {
let count = count.clamp(1, MAX_COUNT);
if do_paste(ed, before, count, cursor_after, reindent) && !vim(ed).replaying {
vim_mut(ed).last_change = Some(LastChange::Paste {
before,
count,
cursor_after,
reindent,
});
}
}
pub fn jump_back_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
for _ in 0..count.max(1) {
if !jump_back(ed) {
break;
}
}
}
pub fn jump_forward_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
count: usize,
) {
for _ in 0..count.max(1) {
if !jump_forward(ed) {
break;
}
}
}
pub fn force_normal_bridge<H: hjkl_engine::types::Host>(ed: &mut Editor<hjkl_buffer::View, H>) {
vim_mut(ed).force_normal();
}
pub fn mouse_click_doc_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
row: usize,
col: usize,
) {
if vim(ed).is_visual() {
vim_mut(ed).force_normal();
}
break_undo_group_in_insert(ed);
let max_row = buf_row_count(ed.buffer()).saturating_sub(1);
let r = row.min(max_row);
let line_len = buf_line(ed.buffer(), r).map_or(0, |l| l.chars().count());
let cap = if vim(ed).current_mode == hjkl_engine::VimMode::Insert {
line_len
} else {
line_len.saturating_sub(1)
};
let c = col.min(cap);
buf_set_cursor_rc(ed.buffer_mut(), r, c);
ed.set_sticky_col(Some(c));
}
pub fn mouse_begin_drag_bridge<H: hjkl_engine::types::Host>(ed: &mut Editor<hjkl_buffer::View, H>) {
if !vim(ed).is_visual_char() {
enter_visual_char_bridge(ed);
}
}
pub fn range_for_op_motion_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
motion_key: char,
total_count: usize,
) -> Option<(usize, usize)> {
let start = ed.cursor();
let input = hjkl_engine::input::Input {
key: hjkl_engine::input::Key::Char(motion_key),
ctrl: false,
alt: false,
shift: false,
};
let motion = parse_motion(&input)?;
let motion = match motion {
Motion::FindRepeat { reverse } => match vim(ed).last_find {
Some((ch, forward, till)) => Motion::Find {
ch,
forward: if reverse { !forward } else { forward },
till,
},
None => return None,
},
m => m,
};
apply_motion_cursor_ctx(ed, &motion, total_count, true);
let end = ed.cursor();
buf_set_cursor_rc(ed.buffer_mut(), start.0, start.1);
let (r0, r1) = (start.0.min(end.0), start.0.max(end.0));
Some((r0, r1))
}
pub fn range_for_op_g_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
ch: char,
total_count: usize,
) -> Option<(usize, usize)> {
let start = ed.cursor();
let motion = match ch {
'g' => Motion::FileTop,
'e' => Motion::WordEndBack,
'E' => Motion::BigWordEndBack,
'j' => Motion::ScreenDown,
'k' => Motion::ScreenUp,
_ => return None,
};
apply_motion_cursor_ctx(ed, &motion, total_count, true);
let end = ed.cursor();
buf_set_cursor_rc(ed.buffer_mut(), start.0, start.1);
let (r0, r1) = (start.0.min(end.0), start.0.max(end.0));
Some((r0, r1))
}
pub fn range_for_op_text_obj_bridge<H: hjkl_engine::types::Host>(
ed: &Editor<hjkl_buffer::View, H>,
ch: char,
inner: bool,
total_count: usize,
) -> Option<(usize, usize)> {
let obj = match ch {
'w' => TextObject::Word { big: false },
'W' => TextObject::Word { big: true },
'"' | '\'' | '`' => TextObject::Quote(ch),
'(' | ')' | 'b' => TextObject::Bracket('('),
'[' | ']' => TextObject::Bracket('['),
'{' | '}' | 'B' => TextObject::Bracket('{'),
'<' | '>' => TextObject::Bracket('<'),
'p' => TextObject::Paragraph,
't' => TextObject::XmlTag,
's' => TextObject::Sentence,
_ => return None,
};
let (start, end, _kind) = text_object_range(ed, obj, inner, total_count.max(1))?;
let (r0, r1) = (start.0.min(end.0), start.0.max(end.0));
Some((r0, r1))
}
pub fn search_repeat_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
forward: bool,
count: usize,
) {
if let Some(pattern) = ed.last_search_pattern() {
ed.push_search_pattern(&pattern);
}
if ed.search_state().pattern.is_none() {
return;
}
let go_forward = ed.last_search_forward() == forward;
for _ in 0..count.max(1) {
if go_forward {
ed.search_advance_forward(true);
} else {
ed.search_advance_backward(true);
}
}
}
pub fn word_search_bridge<H: hjkl_engine::types::Host>(
ed: &mut Editor<hjkl_buffer::View, H>,
forward: bool,
whole_word: bool,
count: usize,
) {
word_at_cursor_search(ed, forward, whole_word, count.max(1));
}