Added rendering, basic keybinds
This commit is contained in:
parent
ac9104273a
commit
2183c97c58
8 changed files with 513 additions and 189 deletions
|
|
@ -7,4 +7,8 @@ license = "MIT"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = "0.28.1"
|
||||
tokio = { version = "1.42.0", features = ["full"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
|
|
|||
236
src/editor/editor.rs
Normal file
236
src/editor/editor.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use color_eyre::Report;
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{self, poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||
execute, queue, style,
|
||||
terminal::{self, ClearType},
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader, Write},
|
||||
sync::{atomic::AtomicBool, mpsc, Arc},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::{runtime::Runtime, sync::oneshot};
|
||||
|
||||
use super::Keymap;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
pub enum Mode {
|
||||
NORMAL,
|
||||
COMMAND,
|
||||
INSERT,
|
||||
VISUAL,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ControlCode {
|
||||
CONTINUE,
|
||||
QUIT,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Editor {
|
||||
buffer: Vec<String>,
|
||||
dirty: bool,
|
||||
stop: bool,
|
||||
|
||||
mode: Mode,
|
||||
|
||||
size: (u16, u16),
|
||||
cursor: (u16, u16),
|
||||
offset: (u16, u16),
|
||||
|
||||
keymap: Keymap,
|
||||
last_key_time: Instant,
|
||||
|
||||
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();
|
||||
|
||||
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),
|
||||
);
|
||||
|
||||
keymap.add_keybind(
|
||||
vec![Mode::NORMAL],
|
||||
vec![
|
||||
KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL),
|
||||
KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL),
|
||||
KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL),
|
||||
],
|
||||
|editor| Ok(editor.buffer.push("Double QUIT".to_string())),
|
||||
);
|
||||
|
||||
let mut editor = Self {
|
||||
buffer: vec![String::new()],
|
||||
dirty: true,
|
||||
stop: false,
|
||||
|
||||
mode: Mode::NORMAL,
|
||||
|
||||
size: terminal::size().unwrap(),
|
||||
cursor: (0, 0),
|
||||
offset: (0, 0),
|
||||
|
||||
keymap,
|
||||
last_key_time: Instant::now(),
|
||||
|
||||
out: io::stdout(),
|
||||
};
|
||||
|
||||
let _ = execute!(editor.out, terminal::EnterAlternateScreen);
|
||||
editor
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, filename: &str) {
|
||||
let file = match File::open(filename) {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
eprintln!("Error opening file '{}': {}", filename, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
self.buffer = match reader.lines().collect::<Result<Vec<String>, _>>() {
|
||||
Ok(lines) => lines,
|
||||
Err(e) => {
|
||||
eprintln!("Error reading lines from file '{}': {}", filename, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), Report> {
|
||||
let (tx, mut rx) = mpsc::channel::<KeyEvent>();
|
||||
|
||||
let rt = Runtime::new()?;
|
||||
rt.block_on(async {
|
||||
tokio::spawn(async move {
|
||||
Editor::key_event_listener(tx).await;
|
||||
});
|
||||
|
||||
while !self.stop {
|
||||
self.handle_key_event(&mut rx).await?;
|
||||
self.render().await?;
|
||||
}
|
||||
|
||||
self.buffer.push("This is outside main loop".to_string());
|
||||
self.dirty = true;
|
||||
self.render().await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async 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;
|
||||
}
|
||||
|
||||
let event = match rx.try_recv() {
|
||||
Ok(event) => event,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
let mut unresolved = self.keymap.traverse(&self.mode, event)?;
|
||||
if unresolved.is_some() {
|
||||
self.execute_keymap_action()?;
|
||||
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()?;
|
||||
}
|
||||
|
||||
self.last_key_time = Instant::now();
|
||||
self.dirty = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_keymap_action(&mut self) -> Result<(), Report> {
|
||||
if let Some(action) = self.keymap.get_action() {
|
||||
action.borrow_mut()(self)?;
|
||||
};
|
||||
|
||||
self.keymap.clear();
|
||||
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() {
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Event::Key(key_event) = read().unwrap() {
|
||||
if tx.send(key_event).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
140
src/editor/keymap.rs
Normal file
140
src/editor/keymap.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
use color_eyre::Report;
|
||||
use crossterm::event::KeyEvent;
|
||||
use std::{cell::RefCell, collections::HashMap, fmt, io, rc::Rc};
|
||||
|
||||
use crate::editor::{ControlCode, Editor, Mode};
|
||||
|
||||
type ActionFn = dyn FnMut(&mut Editor) -> Result<(), Report>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct KeyNode {
|
||||
children: HashMap<KeyEvent, Rc<RefCell<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)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyNode {
|
||||
pub fn new() -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
children: HashMap::new(),
|
||||
action: None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
self.children.is_empty()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, sequence: Vec<KeyEvent>, action: Rc<RefCell<ActionFn>>) {
|
||||
if sequence.is_empty() {
|
||||
self.action = Some(action);
|
||||
return;
|
||||
}
|
||||
|
||||
let (key, sequence) = sequence.split_first().unwrap();
|
||||
let next_node = self.children.entry(key.clone()).or_insert_with(KeyNode::new);
|
||||
|
||||
next_node.borrow_mut().insert(sequence.to_vec(), action)
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_keybind<F>(&mut self, modes: Vec<Mode>, sequence: Vec<KeyEvent>, action: F)
|
||||
where
|
||||
F: FnMut(&mut Editor) -> Result<(), Report> + 'static,
|
||||
{
|
||||
let action = Rc::new(RefCell::new(action));
|
||||
|
||||
for mode in modes {
|
||||
let mut root_node = self.root.entry(mode).or_insert_with(KeyNode::new).borrow_mut();
|
||||
root_node.insert(sequence.clone(), action.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn traverse(&mut self, mode: &Mode, event: KeyEvent) -> Result<Option<KeyEvent>, Report> {
|
||||
let current_node = match self.current {
|
||||
Some(ref node) => node.clone(),
|
||||
None => self.root.get(mode).unwrap().clone(),
|
||||
};
|
||||
|
||||
let next_node = match current_node.borrow().children.get(&event) {
|
||||
Some(node) => node.clone(),
|
||||
None => return Ok(Some(event)),
|
||||
};
|
||||
|
||||
self.current = Some(next_node);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
match self.current {
|
||||
Some(ref node) => node.borrow().is_leaf(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_action(&self) -> Option<Rc<RefCell<ActionFn>>> {
|
||||
if self.current.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.current.as_ref()?.borrow().action.clone()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.current = None;
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.current.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
//impl<W: io::Write> Keymap<W> {
|
||||
// pub fn new() -> Self {
|
||||
// Self {
|
||||
// root: KeyNode::new(),
|
||||
// command: Vec::new(),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn add_keybind<F>(&mut self, modes: Vec<Mode>, sequence: Vec<KeyEvent>, action: F)
|
||||
// where
|
||||
// F: FnMut(&mut Editor<W>) -> io::Result<ControlCode> + 'static,
|
||||
// {
|
||||
// for mode in modes {
|
||||
// self.root.insert(mode, sequence.clone(), action);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn clear(&mut self) {
|
||||
// self.command.clear();
|
||||
// }
|
||||
//
|
||||
// pub fn traverse(&mut self, mode: Mode, event: KeyEvent) -> Option<&mut KeyNode<W>> {
|
||||
// let mut current_node = &mut self.root;
|
||||
//
|
||||
// if let Some(next_node) = current_node.children.get_mut(&(mode, event)) {
|
||||
// self.current_path.push((mode, event));
|
||||
// self.current
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
7
src/editor/mod.rs
Normal file
7
src/editor/mod.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
mod editor;
|
||||
mod keymap;
|
||||
|
||||
pub(crate) use self::editor::{ControlCode, Mode};
|
||||
pub(crate) use self::keymap::Keymap;
|
||||
|
||||
pub use self::editor::Editor;
|
||||
172
src/main.rs
172
src/main.rs
|
|
@ -1,148 +1,37 @@
|
|||
mod rope;
|
||||
mod editor;
|
||||
|
||||
use std::{cmp, io};
|
||||
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{read, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||
execute, queue, style,
|
||||
terminal::{self, ClearType},
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Report;
|
||||
use crossterm::terminal;
|
||||
|
||||
enum Mode {
|
||||
NORMAL,
|
||||
INSERT,
|
||||
VISUAL,
|
||||
}
|
||||
use editor::Editor;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Editor<W: io::Write> {
|
||||
buffer: Vec<String>,
|
||||
command: String,
|
||||
out: W,
|
||||
cursor: (u16, u16),
|
||||
size: (u16, u16),
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl<W: io::Write> Editor<W> {
|
||||
fn new(out: W) -> Self {
|
||||
Self {
|
||||
size: terminal::size().unwrap(),
|
||||
buffer: Vec::new(),
|
||||
command: String::new(),
|
||||
out,
|
||||
cursor: (0, 0),
|
||||
mode: Mode::NORMAL,
|
||||
struct RawModeGuard;
|
||||
impl Drop for RawModeGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = terminal::disable_raw_mode();
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self) -> io::Result<()> {
|
||||
execute!(self.out, terminal::EnterAlternateScreen)?;
|
||||
terminal::enable_raw_mode()?;
|
||||
|
||||
self.buffer.push("".to_string());
|
||||
|
||||
for _ in 1..self.size.0 {
|
||||
self.buffer.push("~".to_string());
|
||||
fn setup() -> Result<(), Report> {
|
||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||
std::env::set_var("RUST_LIB_BACKTRACE", "1")
|
||||
}
|
||||
color_eyre::install()?;
|
||||
|
||||
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();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn teardown(&mut self) -> io::Result<()> {
|
||||
execute!(self.out, style::ResetColor, terminal::LeaveAlternateScreen)?;
|
||||
terminal::disable_raw_mode()
|
||||
}
|
||||
|
||||
fn run(&mut self) -> io::Result<()> {
|
||||
loop {
|
||||
queue!(
|
||||
self.out,
|
||||
style::ResetColor,
|
||||
terminal::Clear(ClearType::CurrentLine),
|
||||
cursor::MoveTo(0, 0)
|
||||
)?;
|
||||
|
||||
for message in &self.buffer {
|
||||
queue!(self.out, style::Print(message), cursor::MoveToNextLine(1))?;
|
||||
}
|
||||
|
||||
queue!(self.out, cursor::MoveTo(self.cursor.0, self.cursor.1))?;
|
||||
|
||||
self.out.flush()?;
|
||||
|
||||
match Self::read_key_event()? {
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('q'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
break;
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('j'),
|
||||
..
|
||||
} => {
|
||||
self.cursor = (self.cursor.0, cmp::min(self.cursor.1 + 1, self.size.1 - 1));
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('k'),
|
||||
..
|
||||
} => {
|
||||
self.cursor = (self.cursor.0, self.cursor.1.saturating_sub(1));
|
||||
}
|
||||
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('h'),
|
||||
..
|
||||
} => {
|
||||
self.cursor = (self.cursor.0.saturating_sub(1), self.cursor.1);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('l'),
|
||||
..
|
||||
} => {
|
||||
self.cursor = (cmp::min(self.cursor.0 + 1, self.size.0 - 1), self.cursor.1);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('d'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
self.cursor = (
|
||||
self.cursor.0,
|
||||
cmp::min(self.cursor.1 + self.size.1 / 2, self.size.1 - 1),
|
||||
);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('u'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
self.cursor = (
|
||||
self.cursor.0,
|
||||
cmp::max(self.cursor.1.saturating_sub(self.size.1 / 2), 0),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_key_event() -> std::io::Result<KeyEvent> {
|
||||
loop {
|
||||
if let Ok(Event::Key(key_event)) = read() {
|
||||
return Ok(key_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
|
|
@ -150,15 +39,22 @@ struct Args {
|
|||
filename: Option<String>,
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
fn main() -> Result<(), Report> {
|
||||
setup()?;
|
||||
|
||||
let args = Args::parse();
|
||||
println!("{:?}", args);
|
||||
|
||||
let mut editor = Editor::new(io::stdout());
|
||||
{
|
||||
terminal::enable_raw_mode()?;
|
||||
let _raw_mode_guard = RawModeGuard;
|
||||
|
||||
//editor.setup()?;
|
||||
//editor.run()?;
|
||||
//editor.teardown()
|
||||
let mut editor = Editor::new();
|
||||
if let Some(filename) = &args.filename {
|
||||
editor.load_file(filename);
|
||||
};
|
||||
|
||||
editor.run()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
51
src/rope.rs
51
src/rope.rs
|
|
@ -1,51 +0,0 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
enum Node {
|
||||
Leaf(String),
|
||||
Internal {
|
||||
left: Rc<Node>,
|
||||
right: Rc<Node>,
|
||||
weight: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Node::Leaf(s) => s.len(),
|
||||
Node::Internal { weight, right, .. } => weight + right.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_leaf(text: &str) -> Rc<Self> {
|
||||
Rc::new(Node::Leaf(text.to_string()))
|
||||
}
|
||||
|
||||
fn new_internal(left: Rc<Self>, right: Rc<Self>) -> Rc<Self> {
|
||||
Rc::new(Node::Internal {
|
||||
weight: left.len(),
|
||||
left,
|
||||
right,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rope {
|
||||
root: Rc<Node>,
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
pub fn new(text: &str) -> Self {
|
||||
Self {
|
||||
root: Node::new_leaf(text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.root.len()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, text: &str) {
|
||||
self.root = self.insert_rec(&self.root, index, text);
|
||||
}
|
||||
}
|
||||
1
src/util/mod.rs
Normal file
1
src/util/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub(crate) mod rope;
|
||||
91
src/util/rope.rs
Normal file
91
src/util/rope.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
enum Node {
|
||||
Leaf(String),
|
||||
Internal {
|
||||
left: Rc<Node>,
|
||||
right: Rc<Node>,
|
||||
weight: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new_leaf(text: &str) -> Rc<Self> {
|
||||
Rc::new(Node::Leaf(text.to_string()))
|
||||
}
|
||||
|
||||
fn new_internal(left: Rc<Self>, right: Rc<Self>) -> Rc<Self> {
|
||||
Rc::new(Node::Internal {
|
||||
weight: left.len(),
|
||||
left,
|
||||
right,
|
||||
})
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Node::Leaf(s) => s.len(),
|
||||
Node::Internal { weight, right, .. } => weight + right.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rope {
|
||||
root: Rc<Node>,
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
pub fn new(text: &str) -> Self {
|
||||
Self {
|
||||
root: Node::new_leaf(text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.root.len()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, text: &str) {
|
||||
self.root = self.insert_rec(&self.root, index, text);
|
||||
}
|
||||
|
||||
fn insert_rec(&self, node: &Rc<Node>, index: usize, text: &str) -> Rc<Node> {
|
||||
match node.as_ref() {
|
||||
Node::Leaf(existing) => {
|
||||
let mut new_text = String::with_capacity(existing.len() + text.len());
|
||||
new_text.push_str(&existing[..index]);
|
||||
new_text.push_str(text);
|
||||
new_text.push_str(&existing[index..]);
|
||||
Node::new_leaf(&new_text)
|
||||
}
|
||||
Node::Internal { left, right, weight } => {
|
||||
if index < *weight {
|
||||
let left = self.insert_rec(left, index, text);
|
||||
return Node::new_internal(left, right.clone());
|
||||
} else {
|
||||
let right = self.insert_rec(right, index - weight, text);
|
||||
return Node::new_internal(left.clone(), right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_text(&self, node: &Rc<Node>, result: &mut String) {
|
||||
match node.as_ref() {
|
||||
Node::Leaf(text) => result.push_str(text),
|
||||
Node::Internal { left, right, .. } => {
|
||||
self.collect_text(left, result);
|
||||
self.collect_text(right, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Rope {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
self.collect_text(&self.root, &mut result);
|
||||
f.write_str(&result)
|
||||
}
|
||||
}
|
||||
Reference in a new issue