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 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 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};
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) = crate::vim::visual::visual_marks_range(ed, &snap);
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
&& !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
}