use hjkl_engine::Editor as EngineEditor;
type Editor<H> = EngineEditor<hjkl_buffer::View, H>;
pub struct StepBookkeeping {
pub pending_was_macro_chord: bool,
pub was_insert: bool,
pub pre_visual_snapshot: Option<crate::vim::LastVisual>,
}
pub(crate) fn begin_step<H: hjkl_engine::Host>(
ed: &mut Editor<H>,
input: hjkl_engine::Input,
) -> Result<StepBookkeeping, bool> {
use crate::vim::{Mode, Pending};
use hjkl_engine::input::Key;
ed.sync_buffer_content_from_textarea();
let now = std::time::Instant::now();
let host_now = ed.host().now();
let timed_out = match ed.last_input_host_at() {
Some(prev) => host_now.saturating_sub(prev) > ed.settings().timeout_len,
None => false,
};
if timed_out {
let chord_in_flight = !matches!(crate::vim_state::vim(ed).pending, Pending::None)
|| crate::vim_state::vim(ed).count != 0
|| crate::vim_state::vim(ed).pending_register.is_some()
|| crate::vim_state::vim(ed).insert_pending_register;
if chord_in_flight {
crate::vim_state::vim_mut(ed).clear_pending_prefix();
}
}
ed.set_last_input_at(Some(now));
ed.set_last_input_host_at(Some(host_now));
if crate::vim_state::vim(ed).recording_macro.is_some()
&& !crate::vim_state::vim(ed).replaying_macro
&& matches!(crate::vim_state::vim(ed).pending, Pending::None)
&& crate::vim_state::vim(ed).mode != Mode::Insert
&& input.key == Key::Char('q')
&& !input.ctrl
&& !input.alt
{
let reg = crate::vim_state::vim_mut(ed)
.recording_macro
.take()
.unwrap();
let keys = std::mem::take(&mut crate::vim_state::vim_mut(ed).recording_keys);
let text = hjkl_engine::input::encode_macro(&keys);
ed.set_named_register_text(reg.to_ascii_lowercase(), text);
return Err(true);
}
let pending_was_macro_chord = matches!(
crate::vim_state::vim(ed).pending,
Pending::RecordMacroTarget | Pending::PlayMacroTarget { .. }
);
let was_insert = crate::vim_state::vim(ed).mode == Mode::Insert;
let pre_visual_snapshot = match crate::vim_state::vim(ed).mode {
Mode::Visual => Some(crate::vim::LastVisual {
mode: Mode::Visual,
anchor: crate::vim_state::vim(ed).visual_anchor,
cursor: ed.cursor(),
block_vcol: 0,
}),
Mode::VisualLine => Some(crate::vim::LastVisual {
mode: Mode::VisualLine,
anchor: (crate::vim_state::vim(ed).visual_line_anchor, 0),
cursor: ed.cursor(),
block_vcol: 0,
}),
Mode::VisualBlock => Some(crate::vim::LastVisual {
mode: Mode::VisualBlock,
anchor: crate::vim_state::vim(ed).block_anchor,
cursor: ed.cursor(),
block_vcol: crate::vim_state::vim(ed).block_vcol,
}),
_ => None,
};
Ok(StepBookkeeping {
pending_was_macro_chord,
was_insert,
pre_visual_snapshot,
})
}
pub(crate) fn end_step<H: hjkl_engine::Host>(
ed: &mut Editor<H>,
input: hjkl_engine::Input,
bk: StepBookkeeping,
consumed: bool,
) -> bool {
use crate::vim::{Mode, Pending};
use hjkl_engine::input::Key;
let StepBookkeeping {
pending_was_macro_chord,
was_insert,
pre_visual_snapshot,
} = bk;
if let Some(snap) = pre_visual_snapshot
&& !matches!(
crate::vim_state::vim(ed).mode,
Mode::Visual | Mode::VisualLine | Mode::VisualBlock
)
{
let (lo, hi) = match snap.mode {
Mode::Visual => {
if snap.anchor <= snap.cursor {
(snap.anchor, snap.cursor)
} else {
(snap.cursor, snap.anchor)
}
}
Mode::VisualLine => {
let r_lo = snap.anchor.0.min(snap.cursor.0);
let r_hi = snap.anchor.0.max(snap.cursor.0);
let vl_rope = ed.buffer().rope();
let r_hi_clamped = r_hi.min(vl_rope.len_lines().saturating_sub(1));
let last_col = hjkl_buffer::rope_line_str(&vl_rope, r_hi_clamped)
.chars()
.count()
.saturating_sub(1);
((r_lo, 0), (r_hi, last_col))
}
Mode::VisualBlock => {
let (r1, c1) = snap.anchor;
let (r2, c2) = snap.cursor;
((r1.min(r2), c1.min(c2)), (r1.max(r2), c1.max(c2)))
}
_ => {
if snap.anchor <= snap.cursor {
(snap.anchor, snap.cursor)
} else {
(snap.cursor, snap.anchor)
}
}
};
ed.set_mark('<', lo);
ed.set_mark('>', hi);
crate::vim_state::vim_mut(ed).last_visual = Some(snap);
}
if !was_insert
&& crate::vim_state::vim(ed).one_shot_normal
&& crate::vim_state::vim(ed).mode == Mode::Normal
&& matches!(crate::vim_state::vim(ed).pending, Pending::None)
{
crate::vim_state::vim_mut(ed).one_shot_normal = false;
crate::vim_state::vim_mut(ed).mode = Mode::Insert;
}
ed.sync_buffer_content_from_textarea();
if !ed.viewport_pinned() {
ed.ensure_cursor_in_scrolloff();
}
ed.set_viewport_pinned(false);
if crate::vim_state::vim(ed).recording_macro.is_some()
&& !crate::vim_state::vim(ed).replaying_macro
&& input.key != Key::Char('q')
&& !pending_was_macro_chord
{
crate::vim_state::vim_mut(ed).recording_keys.push(input);
}
crate::vim_state::vim_mut(ed).current_mode = crate::vim_state::vim_mut(ed).public_mode();
crate::vim::drop_blame_if_left_normal(ed);
consumed
}