#![allow(clippy::missing_const_for_fn)]
use {
reovim_driver_input::KeyEvent, reovim_driver_session::SessionExtension,
reovim_kernel::api::v1::Position,
};
pub use reovim_driver_session::TextObjRange;
#[derive(Debug, Clone, Copy)]
pub struct ReplaceRestoreEntry {
pub position: Position,
pub original: Option<char>,
}
#[derive(Debug, Default)]
pub struct VimSessionState {
pub pending_motion: Option<PendingMotion>,
pub pending_textobj_range: Option<TextObjRange>,
pub pending_char: Option<PendingCharOp>,
pub pending_count: Option<usize>,
pub pending_register: Option<char>,
pub last_change: Option<LastChange>,
pub insert_buffer: String,
pub recording_register: Option<char>,
pub recording_keys: Vec<KeyEvent>,
pub last_macro_register: Option<char>,
pub macro_playback_depth: usize,
pub recording_repeat: bool,
pub repeat_keys: Vec<KeyEvent>,
pub replace_restore_stack: Vec<ReplaceRestoreEntry>,
}
impl SessionExtension for VimSessionState {
fn create() -> Self {
Self::default()
}
}
pub const MAX_MACRO_DEPTH: usize = 16;
impl VimSessionState {
#[must_use]
pub fn is_pending(&self) -> bool {
self.pending_char.is_some()
|| self.pending_count.is_some()
|| self.pending_register.is_some()
}
#[must_use]
pub const fn is_recording(&self) -> bool {
self.recording_register.is_some()
}
#[must_use]
pub const fn is_macro_depth_exceeded(&self) -> bool {
self.macro_playback_depth >= MAX_MACRO_DEPTH
}
pub fn clear_pending(&mut self) {
self.pending_motion = None;
self.pending_textobj_range = None;
self.pending_char = None;
self.pending_count = None;
self.pending_register = None;
}
pub fn take_count(&mut self) -> usize {
self.pending_count.take().unwrap_or(1)
}
pub fn take_register(&mut self) -> Option<char> {
self.pending_register.take()
}
pub fn start_recording(&mut self, register: char) -> bool {
if !register.is_ascii_lowercase() {
return false;
}
self.recording_register = Some(register);
self.recording_keys.clear();
true
}
pub fn stop_recording(&mut self) -> Option<(char, Vec<KeyEvent>)> {
let register = self.recording_register.take()?;
let keys = std::mem::take(&mut self.recording_keys);
Some((register, keys))
}
pub fn record_key(&mut self, key: KeyEvent) {
if self.is_recording() {
self.recording_keys.push(key);
}
}
pub fn start_repeat_recording(&mut self) {
self.recording_repeat = true;
self.repeat_keys.clear();
}
pub fn record_repeat_key(&mut self, key: KeyEvent) {
if self.recording_repeat {
self.repeat_keys.push(key);
}
}
pub fn finish_repeat_recording(&mut self) {
self.recording_repeat = false;
if let Some(ref mut lc) = self.last_change {
lc.keys.clone_from(&self.repeat_keys);
}
self.repeat_keys.clear();
}
pub fn enter_macro_playback(&mut self) -> bool {
if self.is_macro_depth_exceeded() {
return false;
}
self.macro_playback_depth += 1;
true
}
pub fn exit_macro_playback(&mut self) {
self.macro_playback_depth = self.macro_playback_depth.saturating_sub(1);
}
}
#[derive(Debug, Clone, Copy)]
pub struct PendingMotion {
pub linewise: bool,
pub inclusive: bool,
pub word_forward: bool,
}
impl PendingMotion {
#[must_use]
pub const fn new(linewise: bool, inclusive: bool, word_forward: bool) -> Self {
Self {
linewise,
inclusive,
word_forward,
}
}
#[must_use]
pub const fn characterwise() -> Self {
Self {
linewise: false,
inclusive: false,
word_forward: false,
}
}
#[must_use]
pub const fn characterwise_inclusive() -> Self {
Self {
linewise: false,
inclusive: true,
word_forward: false,
}
}
#[must_use]
pub const fn linewise() -> Self {
Self {
linewise: true,
inclusive: false,
word_forward: false,
}
}
#[must_use]
pub const fn word_forward() -> Self {
Self {
linewise: false,
inclusive: false,
word_forward: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PendingCharOp {
FindForward,
FindBackward,
TillForward,
TillBackward,
Replace,
SetMark,
GotoMarkLine,
GotoMarkExact,
}
impl PendingCharOp {
#[must_use]
pub const fn is_find(&self) -> bool {
matches!(self, Self::FindForward | Self::FindBackward)
}
#[must_use]
pub const fn is_till(&self) -> bool {
matches!(self, Self::TillForward | Self::TillBackward)
}
#[must_use]
pub const fn is_motion(&self) -> bool {
!matches!(self, Self::Replace | Self::SetMark | Self::GotoMarkLine | Self::GotoMarkExact)
}
#[must_use]
pub const fn is_forward(&self) -> bool {
matches!(self, Self::FindForward | Self::TillForward)
}
}
#[derive(Debug, Clone)]
pub enum ChangeType {
OperatorMotion {
operator: OperatorType,
linewise: bool,
},
OperatorTextObject {
operator: OperatorType,
linewise: bool,
},
Insert {
text: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperatorType {
Delete,
Yank,
Change,
}
#[derive(Debug, Clone)]
pub struct LastChange {
pub change_type: ChangeType,
pub count: Option<usize>,
pub register: Option<char>,
pub keys: Vec<KeyEvent>,
}