Added buffer insertion and deletion
This commit is contained in:
parent
694c60d386
commit
6e1e8f919e
9 changed files with 378 additions and 135 deletions
|
|
@ -1 +1,2 @@
|
|||
max_width = 120
|
||||
use_small_heuristics = "Max"
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
use color_eyre::Report;
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{poll, read, Event, KeyEvent},
|
||||
execute, queue, style,
|
||||
terminal::{self, ClearType},
|
||||
};
|
||||
use crossterm::event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader, Write},
|
||||
io::{BufRead, BufReader},
|
||||
sync::mpsc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use super::{default_keybinds, Keymap};
|
||||
use crate::util::{Display, Keymap};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
pub enum Mode {
|
||||
NORMAL,
|
||||
COMMAND,
|
||||
|
|
@ -23,54 +18,35 @@ pub enum Mode {
|
|||
VISUAL,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Editor {
|
||||
pub(crate) buffer: Vec<String>,
|
||||
pub(crate) command: String,
|
||||
pub(crate) dirty: bool,
|
||||
pub(crate) stop: bool,
|
||||
|
||||
pub(crate) mode: Mode,
|
||||
|
||||
pub(crate) size: (u16, u16),
|
||||
pub(crate) cursor: (u16, u16),
|
||||
pub(crate) offset: (u16, u16),
|
||||
pub(crate) display: Display,
|
||||
|
||||
pub(crate) keymap: Keymap,
|
||||
pub(crate) last_key_time: Instant,
|
||||
|
||||
pub(crate) out: io::Stdout,
|
||||
}
|
||||
|
||||
impl Drop for Editor {
|
||||
fn drop(&mut self) {
|
||||
let _ = execute!(self.out, style::ResetColor, terminal::LeaveAlternateScreen);
|
||||
}
|
||||
}
|
||||
|
||||
impl Editor {
|
||||
pub fn new() -> Self {
|
||||
let mut keymap = Keymap::new();
|
||||
default_keybinds(&mut keymap);
|
||||
|
||||
let mut editor = Self {
|
||||
Self {
|
||||
buffer: vec![String::new()],
|
||||
command: String::new(),
|
||||
dirty: true,
|
||||
stop: false,
|
||||
|
||||
mode: Mode::NORMAL,
|
||||
|
||||
size: terminal::size().unwrap(),
|
||||
cursor: (0, 0),
|
||||
offset: (0, 0),
|
||||
display: Display::new(),
|
||||
|
||||
keymap,
|
||||
keymap: Keymap::new(),
|
||||
last_key_time: Instant::now(),
|
||||
|
||||
out: io::stdout(),
|
||||
};
|
||||
|
||||
let _ = execute!(editor.out, terminal::EnterAlternateScreen);
|
||||
editor
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, filename: &str) {
|
||||
|
|
@ -100,23 +76,22 @@ impl Editor {
|
|||
tokio::spawn(async move {
|
||||
Editor::key_event_listener(tx).await;
|
||||
});
|
||||
});
|
||||
|
||||
while !self.stop {
|
||||
self.handle_key_event(&mut rx).await?;
|
||||
self.render().await?;
|
||||
}
|
||||
self.handle_key_event(&mut rx)?;
|
||||
|
||||
self.buffer.push("This is outside main loop".to_string());
|
||||
self.dirty = true;
|
||||
self.render().await?;
|
||||
if self.dirty {
|
||||
self.display.render(&self.buffer, &self.command, &self.mode)?;
|
||||
self.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_key_event(&mut self, rx: &mut mpsc::Receiver<KeyEvent>) -> Result<(), Report> {
|
||||
fn handle_key_event(&mut self, rx: &mut mpsc::Receiver<KeyEvent>) -> Result<(), Report> {
|
||||
if self.last_key_time.elapsed().as_millis() > 1000 && !self.keymap.is_empty() {
|
||||
self.buffer.push("TIMEOUT".to_string());
|
||||
self.execute_keymap_action()?;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
|
@ -132,22 +107,69 @@ impl Editor {
|
|||
unresolved = self.keymap.traverse(&self.mode, event)?;
|
||||
}
|
||||
|
||||
if let Some(unresolved) = unresolved {
|
||||
self.buffer.push(format!(
|
||||
"{} {:?} {:?}",
|
||||
unresolved.code, unresolved.modifiers, self.last_key_time
|
||||
));
|
||||
}
|
||||
|
||||
if self.keymap.is_leaf() {
|
||||
self.execute_keymap_action()?;
|
||||
}
|
||||
|
||||
if let Some(unresolved) = unresolved {
|
||||
if !unresolved.modifiers.intersects(KeyModifiers::ALT | KeyModifiers::CONTROL) {
|
||||
self.handle_unresolved_key_event(unresolved);
|
||||
}
|
||||
}
|
||||
|
||||
self.last_key_time = Instant::now();
|
||||
self.dirty = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_unresolved_key_event(&mut self, unresolved: KeyEvent) {
|
||||
match self.mode {
|
||||
Mode::COMMAND => {
|
||||
if let KeyCode::Char(c) = unresolved.code {
|
||||
self.command.push(c);
|
||||
} else if unresolved.code == KeyCode::Backspace {
|
||||
self.command.pop();
|
||||
}
|
||||
}
|
||||
Mode::INSERT => match unresolved.code {
|
||||
KeyCode::Char(c) => {
|
||||
let (x, y) = self.display.cursor.position;
|
||||
self.buffer[y as usize].insert(x as usize, c);
|
||||
self.display.move_cursor((1, 0), &self.buffer);
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
let (x, y) = self.display.cursor.position;
|
||||
let remaining = self.buffer[y as usize].split_off(x as usize);
|
||||
self.buffer.insert((y + 1) as usize, remaining);
|
||||
self.display.move_cursor((-(x as i16), 1), &self.buffer)
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
let (x, y) = self.display.cursor.position;
|
||||
if x < self.buffer[y as usize].len() as u16 {
|
||||
self.buffer[y as usize].remove(x as usize);
|
||||
} else if y + 1 < self.buffer.len() as u16 {
|
||||
let next_line = self.buffer.remove((y + 1) as usize);
|
||||
self.buffer[y as usize].push_str(&next_line);
|
||||
}
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
let (x, y) = self.display.cursor.position;
|
||||
if x > 0 {
|
||||
self.buffer[y as usize].remove((x - 1) as usize);
|
||||
self.display.move_cursor((-1, 0), &self.buffer);
|
||||
} else if y > 0 {
|
||||
let prev_line_len = self.buffer[(y - 1) as usize].len() as u16;
|
||||
let current_line = self.buffer.remove(y as usize);
|
||||
self.buffer[(y - 1) as usize].push_str(¤t_line);
|
||||
self.display.move_cursor((prev_line_len as i16, -1), &self.buffer);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_keymap_action(&mut self) -> Result<(), Report> {
|
||||
if let Some(action) = self.keymap.get_action() {
|
||||
action.borrow_mut()(self)?;
|
||||
|
|
@ -157,44 +179,6 @@ impl Editor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn render(&mut self) -> Result<(), Report> {
|
||||
if !self.dirty {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
queue!(
|
||||
self.out,
|
||||
style::ResetColor,
|
||||
terminal::Clear(ClearType::All),
|
||||
cursor::MoveTo(0, 0)
|
||||
)?;
|
||||
|
||||
let max_lines = self.size.1 as usize;
|
||||
|
||||
let render = &self.buffer[self.offset.1 as usize..]
|
||||
.iter()
|
||||
.take(max_lines)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for line in render {
|
||||
queue!(self.out, style::Print(line), cursor::MoveToNextLine(1))?;
|
||||
}
|
||||
|
||||
let rendered_lines = render.len();
|
||||
if rendered_lines < max_lines {
|
||||
let empty_lines = max_lines - rendered_lines;
|
||||
for _ in 0..empty_lines {
|
||||
queue!(self.out, style::Print("~"), cursor::MoveToNextLine(1))?;
|
||||
}
|
||||
}
|
||||
|
||||
queue!(self.out, cursor::MoveTo(self.cursor.0, self.cursor.1))?;
|
||||
self.out.flush()?;
|
||||
|
||||
self.dirty = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn key_event_listener(tx: mpsc::Sender<KeyEvent>) {
|
||||
loop {
|
||||
if !poll(Duration::from_millis(10)).unwrap() {
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
use super::{Keymap, Mode};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
pub fn default_keybinds(keymap: &mut Keymap) {
|
||||
keymap.add_keybind(
|
||||
vec![Mode::NORMAL],
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL),
|
||||
KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL),
|
||||
],
|
||||
|editor| Ok(editor.stop = true),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
mod editor;
|
||||
mod keybinds;
|
||||
mod keymap;
|
||||
|
||||
pub(crate) use self::editor::{ControlCode, Mode};
|
||||
pub(crate) use self::keybinds::default_keybinds;
|
||||
pub(crate) use self::keymap::Keymap;
|
||||
pub(crate) use self::editor::Mode;
|
||||
|
||||
pub use self::editor::Editor;
|
||||
|
|
|
|||
130
src/macros.rs
Normal file
130
src/macros.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
use crate::editor::{Editor, Mode};
|
||||
|
||||
macro_rules! add_keybind {
|
||||
($editor:expr, $modes:expr, $sequence:expr, $action:expr) => {
|
||||
$editor.keymap.add_keybind(
|
||||
add_keybind!(@parse_modes $modes),
|
||||
add_keybind!(@parse_keys $sequence),
|
||||
$action,
|
||||
);
|
||||
};
|
||||
|
||||
(@parse_keys $keyseq:expr) => {{
|
||||
let mut keys = Vec::new();
|
||||
let mut chars = $keyseq.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '<' {
|
||||
let mut key_name = String::new();
|
||||
while let Some(&peek) = chars.peek() {
|
||||
if peek == '>' {
|
||||
chars.next();
|
||||
break;
|
||||
}
|
||||
key_name.push(chars.next().unwrap());
|
||||
}
|
||||
|
||||
let (modifiers, key) = if let Some(split_idx) = key_name.find('-') {
|
||||
let (modifier, key) = key_name.split_at(split_idx);
|
||||
(modifier, &key[1..].to_string())
|
||||
} else {
|
||||
("", &key_name)
|
||||
};
|
||||
|
||||
let modifiers = match modifiers {
|
||||
"C" => KeyModifiers::CONTROL,
|
||||
"S" => KeyModifiers::SHIFT,
|
||||
"A" => KeyModifiers::ALT,
|
||||
_ => KeyModifiers::NONE,
|
||||
};
|
||||
|
||||
let key_event = match key.as_str() {
|
||||
"BS" => KeyEvent::new(KeyCode::Backspace, modifiers),
|
||||
"Tab" => KeyEvent::new(KeyCode::Tab, modifiers),
|
||||
"CR" | "Enter" | "Return" => KeyEvent::new(KeyCode::Enter, modifiers),
|
||||
"Esc" => KeyEvent::new(KeyCode::Esc, modifiers),
|
||||
"Space" => KeyEvent::new(KeyCode::Char(' '), modifiers),
|
||||
"Up" => KeyEvent::new(KeyCode::Up, modifiers),
|
||||
"Down" => KeyEvent::new(KeyCode::Down, modifiers),
|
||||
"Left" => KeyEvent::new(KeyCode::Left, modifiers),
|
||||
"Right" => KeyEvent::new(KeyCode::Right, modifiers),
|
||||
"Insert" => KeyEvent::new(KeyCode::Insert, modifiers),
|
||||
"Del" => KeyEvent::new(KeyCode::Delete, modifiers),
|
||||
"Home" => KeyEvent::new(KeyCode::Home, modifiers),
|
||||
"End" => KeyEvent::new(KeyCode::End, modifiers),
|
||||
"PageUp" => KeyEvent::new(KeyCode::PageUp, modifiers),
|
||||
"PageDown" => KeyEvent::new(KeyCode::PageDown, modifiers),
|
||||
key if key.starts_with('F') && key[1..].parse::<u8>().is_ok() => {
|
||||
let f_num = key[1..].parse::<u8>().unwrap();
|
||||
KeyEvent::new(KeyCode::F(f_num), modifiers)
|
||||
}
|
||||
key if key.len() == 1 => {
|
||||
KeyEvent::new(KeyCode::Char(key.chars().next().unwrap()), modifiers)
|
||||
}
|
||||
_ => panic!("Unknown key: {}", key),
|
||||
};
|
||||
|
||||
keys.push(key_event);
|
||||
} else {
|
||||
keys.push(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE));
|
||||
}
|
||||
}
|
||||
|
||||
keys
|
||||
}};
|
||||
|
||||
(@parse_modes $modes:expr) => {{
|
||||
$modes
|
||||
.chars()
|
||||
.filter_map(|c| match c {
|
||||
'n' => Some(Mode::NORMAL),
|
||||
'v' => Some(Mode::VISUAL),
|
||||
'c' => Some(Mode::COMMAND),
|
||||
'i' => Some(Mode::INSERT),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn default_keybinds(editor: &mut Editor) {
|
||||
add_keybind!(editor, "ni", "<C-w><C-q>", |e| Ok(e.stop = true));
|
||||
|
||||
add_keybind!(editor, "n", "k", |e| {
|
||||
e.display.move_cursor((0, -1), &e.buffer);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "n", "j", |e| {
|
||||
e.display.move_cursor((0, 1), &e.buffer);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "n", "h", |e| {
|
||||
e.display.move_cursor((-1, 0), &e.buffer);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "n", "l", |e| {
|
||||
e.display.move_cursor((1, 0), &e.buffer);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "n", "i", |e| {
|
||||
e.mode = Mode::INSERT;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "n", ":", |e| {
|
||||
e.mode = Mode::COMMAND;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
add_keybind!(editor, "ic", "<Esc>", |e| {
|
||||
e.mode = Mode::NORMAL;
|
||||
e.command.clear();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
13
src/main.rs
13
src/main.rs
|
|
@ -1,13 +1,16 @@
|
|||
mod editor;
|
||||
mod util;
|
||||
|
||||
use std::time::Instant;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Report;
|
||||
use crossterm::terminal;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use editor::Editor;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use macros::default_keybinds;
|
||||
|
||||
struct RawModeGuard;
|
||||
impl Drop for RawModeGuard {
|
||||
|
|
@ -25,9 +28,7 @@ fn setup() -> Result<(), Report> {
|
|||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", "info")
|
||||
}
|
||||
tracing_subscriber::fmt::fmt()
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.init();
|
||||
tracing_subscriber::fmt::fmt().with_env_filter(EnvFilter::from_default_env()).init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -49,6 +50,8 @@ fn main() -> Result<(), Report> {
|
|||
let _raw_mode_guard = RawModeGuard;
|
||||
|
||||
let mut editor = Editor::new();
|
||||
default_keybinds(&mut editor);
|
||||
|
||||
if let Some(filename) = &args.filename {
|
||||
editor.load_file(filename);
|
||||
};
|
||||
|
|
|
|||
151
src/util/display.rs
Normal file
151
src/util/display.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
use color_eyre::Report;
|
||||
use crossterm::{
|
||||
cursor::{self, SetCursorStyle},
|
||||
execute, queue,
|
||||
style::{self, Stylize},
|
||||
terminal::{self, ClearType},
|
||||
};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::editor::Mode;
|
||||
|
||||
pub struct Display {
|
||||
size: (u16, u16),
|
||||
offset: (u16, u16),
|
||||
|
||||
pub(crate) cursor: Cursor,
|
||||
|
||||
pub(crate) out: io::Stdout,
|
||||
}
|
||||
|
||||
pub struct Cursor {
|
||||
pub(crate) position: (u16, u16),
|
||||
pub(crate) max_column: u16,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
pub fn new() -> Self {
|
||||
Self { position: (0, 0), max_column: 0 }
|
||||
}
|
||||
|
||||
fn move_by(&mut self, delta: (i16, i16), buffer: &Vec<String>) {
|
||||
let saturate = |pos: u16, delta: i16| {
|
||||
if delta.is_negative() {
|
||||
pos.saturating_sub(delta.abs() as u16)
|
||||
} else {
|
||||
pos.saturating_add(delta as u16)
|
||||
}
|
||||
};
|
||||
|
||||
let (mut x, mut y) = self.position;
|
||||
let (dx, dy) = delta;
|
||||
|
||||
if dx != 0 {
|
||||
x = saturate(x, dx);
|
||||
self.max_column = x;
|
||||
}
|
||||
|
||||
if dy != 0 {
|
||||
y = saturate(y, dy);
|
||||
}
|
||||
|
||||
self.position = (x, y);
|
||||
self.validate_cursor(buffer);
|
||||
}
|
||||
|
||||
fn move_x(&mut self, new_x: u16) {
|
||||
self.position.0 = new_x;
|
||||
}
|
||||
|
||||
fn move_y(&mut self, new_y: u16) {
|
||||
self.position.1 = new_y;
|
||||
}
|
||||
|
||||
fn validate_cursor(&mut self, buffer: &Vec<String>) {
|
||||
let (_x, y) = self.position;
|
||||
|
||||
if y >= buffer.len() as u16 {
|
||||
self.move_y(buffer.len().saturating_sub(1) as u16);
|
||||
}
|
||||
|
||||
let line_len = buffer[self.position.1 as usize].len() as u16;
|
||||
self.move_x(self.max_column.min(line_len));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
fn drop(&mut self) {
|
||||
let _ = execute!(self.out, style::ResetColor, terminal::LeaveAlternateScreen);
|
||||
}
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new() -> Self {
|
||||
let mut display =
|
||||
Self { size: terminal::size().unwrap(), offset: (0, 0), cursor: Cursor::new(), out: io::stdout() };
|
||||
|
||||
let _ = execute!(display.out, terminal::EnterAlternateScreen);
|
||||
|
||||
display
|
||||
}
|
||||
|
||||
pub fn move_cursor(&mut self, delta: (i16, i16), buffer: &Vec<String>) {
|
||||
self.cursor.move_by(delta, buffer);
|
||||
}
|
||||
|
||||
pub fn render(&mut self, buffer: &Vec<String>, command: &String, mode: &Mode) -> Result<(), Report> {
|
||||
queue!(self.out, style::ResetColor, terminal::Clear(ClearType::All), cursor::MoveTo(0, 0))?;
|
||||
|
||||
let mut max_lines = self.size.1 as usize;
|
||||
if !command.is_empty() {
|
||||
max_lines -= 1;
|
||||
}
|
||||
|
||||
let max_columns = self.size.0 as usize;
|
||||
|
||||
let render = buffer[self.offset.1 as usize..]
|
||||
.iter()
|
||||
.take(max_lines)
|
||||
.map(|line| {
|
||||
if line.len() > self.offset.0 as usize {
|
||||
let start = self.offset.0 as usize;
|
||||
let end = (start + max_columns).min(line.len());
|
||||
&line[start..end]
|
||||
} else {
|
||||
""
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for line in &render {
|
||||
queue!(self.out, style::Print(line), cursor::MoveToNextLine(1))?;
|
||||
}
|
||||
|
||||
let rendered_lines = render.len();
|
||||
if rendered_lines < max_lines {
|
||||
let empty_lines = max_lines - rendered_lines;
|
||||
for _ in 0..empty_lines {
|
||||
queue!(self.out, style::Print("~"), cursor::MoveToNextLine(1))?;
|
||||
}
|
||||
}
|
||||
|
||||
if !command.is_empty() {
|
||||
queue!(
|
||||
self.out,
|
||||
style::SetAttribute(style::Attribute::Bold),
|
||||
style::Print(command),
|
||||
style::SetAttribute(style::Attribute::Reset),
|
||||
cursor::MoveToNextLine(1)
|
||||
)?;
|
||||
}
|
||||
|
||||
match mode {
|
||||
Mode::INSERT => queue!(self.out, cursor::SetCursorStyle::BlinkingBar)?,
|
||||
_ => queue!(self.out, cursor::SetCursorStyle::DefaultUserShape)?,
|
||||
}
|
||||
|
||||
queue!(self.out, cursor::MoveTo(self.cursor.position.0, self.cursor.position.1))?;
|
||||
self.out.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
use color_eyre::Report;
|
||||
use crossterm::event::KeyEvent;
|
||||
use std::{cell::RefCell, collections::HashMap, fmt, io, rc::Rc};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::editor::{ControlCode, Editor, Mode};
|
||||
use crate::editor::{Editor, Mode};
|
||||
|
||||
type ActionFn = dyn FnMut(&mut Editor) -> Result<(), Report>;
|
||||
|
||||
|
|
@ -12,18 +12,14 @@ struct KeyNode {
|
|||
action: Option<Rc<RefCell<ActionFn>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for KeyNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "KeyNode {{ children: {:?} }}", self.children)
|
||||
}
|
||||
pub struct Keymap {
|
||||
root: HashMap<Mode, Rc<RefCell<KeyNode>>>,
|
||||
current: Option<Rc<RefCell<KeyNode>>>,
|
||||
}
|
||||
|
||||
impl KeyNode {
|
||||
pub fn new() -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
children: HashMap::new(),
|
||||
action: None,
|
||||
}))
|
||||
Rc::new(RefCell::new(Self { children: HashMap::new(), action: None }))
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
|
|
@ -43,18 +39,9 @@ impl KeyNode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Keymap {
|
||||
root: HashMap<Mode, Rc<RefCell<KeyNode>>>,
|
||||
current: Option<Rc<RefCell<KeyNode>>>,
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
root: HashMap::new(),
|
||||
current: None,
|
||||
}
|
||||
Self { root: HashMap::new(), current: None }
|
||||
}
|
||||
|
||||
pub fn add_keybind<F>(&mut self, modes: Vec<Mode>, sequence: Vec<KeyEvent>, action: F)
|
||||
|
|
@ -1 +1,5 @@
|
|||
pub(crate) mod rope;
|
||||
pub(crate) mod display;
|
||||
pub(crate) mod keymap;
|
||||
|
||||
pub(crate) use self::display::Display;
|
||||
pub(crate) use self::keymap::Keymap;
|
||||
|
|
|
|||
Reference in a new issue