From a82b0dfe4f2ad8661a5538bacdf4d952285ef3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Macio=C5=82ek?= Date: Fri, 14 Jun 2024 23:30:56 +0200 Subject: [PATCH] gap buffer p5 --- src/app.rs | 42 +++++++++++++++++++-------------- src/gap_buffer.rs | 59 +++++++++++++++++++++++++++++++++++++++++++---- src/ui.rs | 4 ++-- 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/app.rs b/src/app.rs index 151679a..6e14fbb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,8 +2,11 @@ use std::{cmp::min, error, fs}; use ratatui::layout::Rect; +use crate::gap_buffer::GapBuffer; + pub type AppResult = std::result::Result>; +const GAP_BUFFER_DEFAULT_SIZE: usize = 80; const QUIT_TIMES: i8 = 2; const DEFAULT_STATUS: &str = "Press Ctrl + C to quit, Ctrl + S to save."; @@ -22,7 +25,7 @@ pub struct Position { #[derive(Debug)] pub struct App { pub running: bool, - pub content: Vec, + pub content: Vec, pub cursor_position: Position, pub cursor_offset: Position, pub opened_filename: String, @@ -40,7 +43,7 @@ impl Default for App { fn default() -> Self { Self { running: true, - content: vec![String::new()], + content: vec![], cursor_position: Position { x: 0, y: 0 }, cursor_offset: Position { x: 0, y: 0 }, opened_filename: String::new(), @@ -103,12 +106,12 @@ impl App { std::cmp::max((self.content.len() as f64).log10().ceil() as usize, 4); } - fn push_to_content(&mut self, s: String) { + fn push_to_content(&mut self, s: GapBuffer) { self.content.push(s); self.update_line_numbers_width(); } - fn insert_to_content(&mut self, index: usize, s: String) { + fn insert_to_content(&mut self, index: usize, s: GapBuffer) { self.content.insert(index, s); self.update_line_numbers_width(); } @@ -117,7 +120,7 @@ impl App { let s = self.content.remove(index); self.line_numbers_width = std::cmp::max((self.content.len() as f64).log10().ceil() as usize, 4); - s + s.to_string() } pub fn enter_prompt(&mut self) { @@ -144,7 +147,14 @@ impl App { self.dirty = false; self.status = format!("Saved to {}", self.opened_filename); - let _ = fs::write(&self.opened_filename, self.content.join("\n")); + let _ = fs::write( + &self.opened_filename, + self.content + .iter() + .map(|b| b.to_string()) + .collect::>() + .join("\n"), + ); } pub fn insert_char(&mut self, c: char) { @@ -155,11 +165,11 @@ impl App { } while self.cursor_position.y >= self.content.len() { - self.push_to_content(String::new()); + self.push_to_content(GapBuffer::new(GAP_BUFFER_DEFAULT_SIZE)); } self.content[self.cursor_position.y + self.cursor_offset.y] - .insert(self.cursor_position.x + self.cursor_offset.x, c); + .insert_at(self.cursor_position.x + self.cursor_offset.x, c); self.move_cursor(Direction { x: 1, y: 0 }); } @@ -172,22 +182,20 @@ impl App { } while self.cursor_position.y >= self.content.len() { - self.push_to_content(String::new()); + self.push_to_content(GapBuffer::new(GAP_BUFFER_DEFAULT_SIZE)); } - let current_line = &self.content[self.cursor_position.y]; + let current_line = &mut self.content[self.cursor_position.y]; if current_line.len() > 0 { - let new_line = self.content[self.cursor_position.y].split_off(self.cursor_position.x); + let new_line = current_line.split_off(self.cursor_position.x); + let index = self.cursor_position.y + self.cursor_offset.y + 1; - self.insert_to_content( - self.cursor_position.y + self.cursor_offset.y + 1, - String::from(new_line), - ); + self.insert_to_content(index, new_line); } else { self.insert_to_content( self.cursor_position.y + self.cursor_offset.y + 1, - String::new(), + GapBuffer::new(GAP_BUFFER_DEFAULT_SIZE), ); } @@ -224,7 +232,7 @@ impl App { self.move_cursor(Direction { x: 0, y: 1 }); } else if !(self.cursor_position.x == 0 && self.cursor_position.y == 0) { - self.content[pos.y].remove(pos.x - 1); + self.content[pos.y].remove_at(pos.x - 1); self.move_cursor(Direction { x: -1, y: 0 }); } diff --git a/src/gap_buffer.rs b/src/gap_buffer.rs index 4b6c68c..8e620e7 100644 --- a/src/gap_buffer.rs +++ b/src/gap_buffer.rs @@ -1,11 +1,12 @@ -struct GapBuffer { +#[derive(Debug, Clone)] +pub struct GapBuffer { buffer: Vec, gap_start: usize, gap_end: usize, } impl GapBuffer { - fn new(capacity: usize) -> GapBuffer { + pub fn new(capacity: usize) -> GapBuffer { GapBuffer { buffer: vec![' '; capacity], gap_start: 0, @@ -13,7 +14,7 @@ impl GapBuffer { } } - fn insert(&mut self, c: char) { + pub fn push(&mut self, c: char) { if self.gap_start == self.gap_end { self.resize(); } @@ -39,7 +40,7 @@ impl GapBuffer { } } - fn move_cursor(&mut self, index: usize) { + fn move_gap(&mut self, index: usize) { if index < self.gap_start { let move_size = self.gap_start - index; self.buffer @@ -55,11 +56,59 @@ impl GapBuffer { } } - fn to_string(&self) -> String { + pub fn to_string(&self) -> String { let mut result: Vec = Vec::with_capacity(self.buffer.len() - (self.gap_end - self.gap_start)); result.extend_from_slice(&self.buffer[..self.gap_start]); result.extend_from_slice(&self.buffer[self.gap_end..]); result.into_iter().collect() } + + pub fn insert_at(&mut self, index: usize, c: char) { + if index > self.buffer.len() { + return; + } + self.move_gap(index); + self.push(c); + } + + pub fn push_str(&mut self, s: &str) { + for c in s.chars() { + self.push(c); + } + } + + pub fn remove_at(&mut self, index: usize) { + if index > self.buffer.len() - (self.gap_end - self.gap_start) { + return; + } + self.move_gap(index + 1); + self.delete(); + } + + pub fn len(&self) -> usize { + self.buffer.len() - (self.gap_end - self.gap_start) + } + + pub fn split_off(&mut self, at: usize) -> GapBuffer { + if at > self.len() { + panic!("Index out of bounds"); + } + self.move_gap(at); + + let new_capacity = self.buffer.len() - self.gap_end; + let mut new_buffer = vec![' '; new_capacity]; + + new_buffer[..new_capacity].copy_from_slice(&self.buffer[self.gap_end..]); + + let split_buffer = GapBuffer { + buffer: new_buffer, + gap_start: 0, + gap_end: new_capacity, + }; + + self.gap_end = self.buffer.len(); + + split_buffer + } } diff --git a/src/ui.rs b/src/ui.rs index 06d4cb4..d9247a2 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -22,10 +22,10 @@ pub fn render(app: &mut App, frame: &mut Frame) { .enumerate() .map(|(i, s)| { if i == pos.y { - Line::from(format!("{: