use crossterm::event::{KeyCode as CtCode, KeyEvent, KeyModifiers};
use vim_line::{Action, Key, KeyCode, LineEditor, VimLineEditor};
pub enum Outcome {
Continue,
Submit(String),
HistoryPrev,
HistoryNext,
Cancel,
}
pub struct Editor {
inner: VimLineEditor,
text: String,
}
impl Default for Editor {
fn default() -> Self {
Self {
inner: VimLineEditor::new(),
text: String::new(),
}
}
}
impl Editor {
pub fn text(&self) -> String {
self.text.clone()
}
pub fn cursor_col(&self) -> usize {
let b = self.inner.cursor().min(self.text.len());
self.text
.get(..b)
.map(|s| s.chars().count())
.unwrap_or_else(|| self.text.chars().count())
}
pub fn status(&self) -> &str {
self.inner.status()
}
pub fn clear(&mut self) {
self.text.clear();
self.inner.reset();
}
pub fn set(&mut self, s: &str) {
self.text = s.to_string();
let end = self.text.len();
self.inner.set_cursor(end, &self.text);
}
pub fn handle(&mut self, event: KeyEvent) -> Outcome {
let Some(key) = to_key(event) else {
return Outcome::Continue;
};
let result = self.inner.handle_key(key, &self.text);
for edit in result.edits.into_iter().rev() {
edit.apply(&mut self.text);
}
match result.action {
Some(Action::Submit) => {
let line = std::mem::take(&mut self.text);
self.inner.reset();
Outcome::Submit(line)
}
Some(Action::SubmitCommand(cmd)) => {
self.inner.reset();
Outcome::Submit(format!(":{cmd}"))
}
Some(Action::HistoryPrev) => Outcome::HistoryPrev,
Some(Action::HistoryNext) => Outcome::HistoryNext,
Some(Action::Cancel) => Outcome::Cancel,
Some(Action::HistorySearch(_) | Action::HistoryAccept | Action::HistoryCancel) => {
Outcome::Continue
}
None => Outcome::Continue,
}
}
}
fn to_key(event: KeyEvent) -> Option<Key> {
let code = match event.code {
CtCode::Char(c) => KeyCode::Char(c),
CtCode::Esc => KeyCode::Escape,
CtCode::Backspace => KeyCode::Backspace,
CtCode::Delete => KeyCode::Delete,
CtCode::Left => KeyCode::Left,
CtCode::Right => KeyCode::Right,
CtCode::Up => KeyCode::Up,
CtCode::Down => KeyCode::Down,
CtCode::Home => KeyCode::Home,
CtCode::End => KeyCode::End,
CtCode::Tab => KeyCode::Tab,
CtCode::Enter => KeyCode::Enter,
_ => return None,
};
let m = event.modifiers;
Some(Key {
code,
ctrl: m.contains(KeyModifiers::CONTROL),
alt: m.contains(KeyModifiers::ALT),
shift: m.contains(KeyModifiers::SHIFT),
})
}