use std::sync::Mutex;
use duat_base::modes::{IncSearch, RunCommands, SearchFwd};
use duat_core::mode::{ctrl, shift};
#[cfg(feature = "treesitter")]
use duat_treesitter::TsHandle;
use crate::{
prelude::{
Handle, cmd,
data::Pass,
mode::{
self,
KeyCode::{self, *},
KeyEvent, event,
},
},
widgets::Buffer,
};
#[derive(Clone)]
pub struct Regular;
impl mode::Mode for Regular {
type Widget = Buffer;
fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>) {
static LAST_CODE: Mutex<Option<KeyCode>> = Mutex::new(None);
let mut last_code = LAST_CODE.lock().unwrap();
match key_event {
event!(Char(char)) => {
if !matches!(*last_code, Some(Char(_))) || char == ' ' {
handle.write(pa).text_mut().new_moment();
}
handle.edit_all(pa, |mut c| {
c.replace(char);
c.unset_anchor();
c.move_hor(1);
})
}
event!(Enter) => {
handle.write(pa).text_mut().new_moment();
handle.edit_all(pa, |mut c| {
#[cfg(not(feature = "treesitter"))]
let indent = c.indent();
c.replace('\n');
c.unset_anchor();
c.move_hor(1);
#[cfg(not(feature = "treesitter"))]
c.insert(" ".repeat(indent));
});
#[cfg(feature = "treesitter")]
if let Some(indents) = handle.ts_get_indentations(pa, ..) {
let mut indents = indents.into_iter();
handle.edit_all(pa, |mut c| {
duatmode::reindent(c.indent(), indents.next().unwrap(), &mut c);
});
}
}
event!(Backspace) => {
let mut major_removal = false;
handle.edit_all(pa, |mut c| {
if c.anchor().is_some()
|| (c.move_hor(-1) != 0
&& c.set_anchor_if_needed()
&& c.selection().chars().any(|c| c == '\n'))
{
major_removal = true;
}
});
if !matches!(*last_code, Some(Backspace)) || major_removal {
handle.write(pa).text_mut().new_moment();
}
handle.edit_all(pa, |mut c| {
c.replace("");
c.unset_anchor();
})
}
event!(Delete) => {
let mut major_removal = false;
handle.edit_all(pa, |mut c| {
c.set_anchor_if_needed();
major_removal |= c.selection().chars().any(|c| c == '\n');
});
if !matches!(*last_code, Some(Delete)) || major_removal {
handle.write(pa).text_mut().new_moment();
}
handle.edit_all(pa, |mut c| {
c.replace("");
c.unset_anchor();
})
}
event!(Left) => handle.edit_all(pa, |mut c| {
c.unset_anchor();
c.move_hor(-1);
}),
event!(Right) => handle.edit_all(pa, |mut c| {
c.unset_anchor();
c.move_hor(1);
}),
event!(Up) => handle.edit_all(pa, |mut c| {
c.unset_anchor();
c.move_ver(-1);
}),
event!(Down) => handle.edit_all(pa, |mut c| {
c.unset_anchor();
c.move_ver(1);
}),
shift!(Left) => handle.edit_all(pa, |mut c| {
c.set_anchor_if_needed();
c.move_hor(-1);
}),
shift!(Right) => handle.edit_all(pa, |mut c| {
c.set_anchor_if_needed();
c.move_hor(1);
}),
shift!(Up) => handle.edit_all(pa, |mut c| {
c.set_anchor_if_needed();
c.move_ver(-1);
}),
shift!(Down) => handle.edit_all(pa, |mut c| {
c.set_anchor_if_needed();
c.move_ver(1);
}),
ctrl!('z') => handle.write(pa).text_mut().undo(),
ctrl!('y' | 'Z') => handle.write(pa).text_mut().redo(),
ctrl!(char @ ('x' | 'c')) => {
if char == 'x' {
handle.write(pa).text_mut().new_moment();
}
let mut prev = Vec::new();
handle.edit_all(pa, |mut c| {
prev.push((c.range(), c.anchor_is_start()));
if c.anchor().is_none() {
let range = c.text().line(c.caret().line()).byte_range();
c.move_to(range);
}
});
crate::mode::copy_selections(pa, &handle);
let mut ranges = prev.into_iter();
handle.edit_all(pa, |mut c| {
if char == 'x' {
c.replace("");
} else {
let (range, anchor_is_start) = ranges.next().unwrap();
c.move_to(range);
if !anchor_is_start {
c.swap_ends();
}
}
});
}
ctrl!('v') => {
let pastes = crate::mode::paste_strings();
if !pastes.is_empty() {
handle.write(pa).text_mut().new_moment();
let mut p_iter = pastes.iter().cycle();
handle.edit_all(pa, |mut c| {
let paste = p_iter.next().unwrap();
if !paste.is_empty() {
let mut c = c.copy();
c.set_caret_on_start();
c.unset_anchor();
if paste.ends_with('\n') {
c.move_to_col(0);
c.insert(paste);
} else {
c.insert(paste)
}
c.destroy();
}
c.move_hor(paste.chars().count() as i32);
if c.anchor().is_some() {
c.swap_ends();
c.move_hor(paste.chars().count() as i32);
c.swap_ends();
}
});
}
}
ctrl!('f') => mode::set(pa, IncSearch::new(SearchFwd)),
ctrl!('P') | event!(F(1)) => mode::set(pa, RunCommands::new()),
ctrl!('p') => mode::set(pa, RunCommands::new_with("edit ")),
ctrl!('n') => mode::set(pa, RunCommands::new_with("open ")),
event!('s') => cmd::queue_notify("write"),
event!('w') => cmd::queue_notify("write-quit"),
event!(',') => cmd::queue_notify("open --cfg"),
_ => {}
}
*last_code = Some(key_event.code);
}
}