use crate::canvas::modes::AppMode;
use crate::canvas::state::SelectionState;
use crate::textarea::actions::selection::helix::HelixCase;
use crate::textarea::{TextAreaDataProvider, TextAreaState};
#[derive(Clone, Copy)]
pub(crate) enum VimPending {
Find {
till: bool,
forward: bool,
count: usize,
},
Replace { count: usize },
}
#[derive(Clone, Copy)]
pub(crate) struct VimFind {
pub ch: char,
pub till: bool,
pub forward: bool,
}
impl<P: TextAreaDataProvider> TextAreaState<P> {
fn field_chars_vim(&self, field: usize) -> Vec<char> {
self.editor
.data_provider()
.field_value(field)
.chars()
.collect()
}
pub(crate) fn yank_primary_selection_vim(&mut self) {
self.yank_selection();
self.exit_highlight_mode_vim();
}
pub(crate) fn delete_selection_vim(&mut self, yank: bool, count: usize) {
for _ in 0..count.max(1) {
if !self.delete_selection_once(yank) {
break;
}
}
self.exit_highlight_mode_vim();
}
pub(crate) fn change_selection_vim(&mut self, count: usize) {
for _ in 0..count.max(1) {
if !self.delete_selection_once(true) {
break;
}
}
self.enter_edit_mode_vim();
#[cfg(feature = "gui")]
{
self.edited_this_frame = true;
}
}
pub(crate) fn switch_case_selection_vim(&mut self) {
self.switch_case_selection_helix(HelixCase::Toggle);
self.exit_highlight_mode_vim();
}
pub(crate) fn indent_selection_vim(&mut self, count: usize) {
self.indent_selection_helix(count);
self.exit_highlight_mode_vim();
}
pub(crate) fn unindent_selection_vim(&mut self, count: usize) {
self.unindent_selection_helix(count);
self.exit_highlight_mode_vim();
}
pub(crate) fn toggle_case_char_vim(&mut self, count: usize) {
let field = self.current_field();
let len = self.field_chars_vim(field).len();
if len == 0 {
return;
}
let cursor = self.cursor_position();
let end = (cursor + count.max(1) - 1).min(len - 1);
self.editor.ui_state.selection = SelectionState::Characterwise {
anchor: (field, cursor),
};
self.editor.ui_state.set_cursor(end, len, false);
self.switch_case_selection_helix(HelixCase::Toggle);
let next = (end + 1).min(len.saturating_sub(1));
self.editor.ui_state.set_cursor(next, len, false);
self.editor.ui_state.selection = SelectionState::None;
}
pub(crate) fn replace_char_vim(&mut self, ch: char, count: usize) {
let field = self.current_field();
let len = self.field_chars_vim(field).len();
let cursor = self.cursor_position();
let count = count.max(1);
if len == 0 || cursor + count > len {
return;
}
let end = cursor + count - 1;
self.editor.ui_state.selection = SelectionState::Characterwise {
anchor: (field, cursor),
};
self.editor.ui_state.set_cursor(end, len, false);
self.replace_selection_with_char_helix(ch);
self.editor.ui_state.set_cursor(end, len, false);
self.editor.ui_state.selection = SelectionState::None;
}
pub(crate) fn find_char_vim(&mut self, ch: char, till: bool, forward: bool, count: usize) {
let field = self.current_field();
let line = self.field_chars_vim(field);
let len = line.len();
let cursor = self.cursor_position();
let count = count.max(1);
let mut pos = cursor;
if forward {
for _ in 0..count {
match ((pos + 1)..len).find(|&j| line[j] == ch) {
Some(hit) => pos = hit,
None => return,
}
}
if till {
if pos == cursor + 1 && count == 1 {
return;
}
pos = pos.saturating_sub(1);
}
} else {
for _ in 0..count {
if pos == 0 {
return;
}
match (0..pos).rev().find(|&j| line[j] == ch) {
Some(hit) => pos = hit,
None => return,
}
}
if till {
pos = (pos + 1).min(len.saturating_sub(1));
}
}
if pos == cursor {
return;
}
if self.mode() == AppMode::Sel {
let anchor = match self.selection_state() {
SelectionState::Characterwise { anchor } => *anchor,
_ => (field, cursor),
};
self.editor.ui_state.set_cursor(pos, len, false);
self.editor.ui_state.selection = SelectionState::Characterwise { anchor };
} else {
self.editor.ui_state.set_cursor(pos, len, false);
self.editor.ui_state.selection = SelectionState::None;
}
}
pub(crate) fn repeat_last_find_vim(&mut self, reverse: bool, count: usize) {
if let Some(find) = self.vim_last_find {
let forward = if reverse { !find.forward } else { find.forward };
self.find_char_vim(find.ch, find.till, forward, count);
}
}
pub(crate) fn set_vim_pending(&mut self, pending: VimPending) {
self.vim_pending = Some(pending);
}
pub(crate) fn resolve_vim_pending(&mut self, pending: VimPending, ch: char) {
match pending {
VimPending::Find {
till,
forward,
count,
} => {
self.vim_last_find = Some(VimFind { ch, till, forward });
self.find_char_vim(ch, till, forward, count);
}
VimPending::Replace { count } => self.replace_char_vim(ch, count),
}
}
}