mod buf; use std::io::{self, Write}; use crate::cursor::{self, Direction}; pub use buf::LineBuf; pub struct Line { buf: LineBuf, dirty: bool, } impl std::ops::Deref for Line { type Target = LineBuf; fn deref(&self) -> &Self::Target { &self.buf } } impl Line { pub fn new() -> Self { Self { buf: LineBuf::new(), dirty: false, } } pub fn mark_clean(&mut self) { self.dirty = false; } pub fn mark_dirty(&mut self) { self.dirty = true; } pub fn is_dirty(&self) -> bool { self.dirty } pub fn had_contents(&self) -> bool { self.buf.is_dirty() } pub fn is_empty(&self) -> bool { self.buf.is_empty() } pub fn cursor_left(&mut self) -> io::Result<()> { if self.buf.left() { let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Left, 1, &mut stdout)?; stdout.flush()?; } Ok(()) } pub fn cursor_right(&mut self) -> io::Result<()> { if self.buf.right() { let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Right, 1, &mut stdout)?; stdout.flush()?; } Ok(()) } pub fn move_to_begin(&mut self) -> io::Result<()> { let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Left, self.buf.all_left(), &mut stdout)?; stdout.flush() } pub fn move_to_end(&mut self) -> io::Result<()> { let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Right, self.buf.all_right(), &mut stdout)?; stdout.flush() } // move to next pub fn cursor_left_word(&mut self) -> io::Result<()> { let mut i = 0; // find word while let Some(b' ') = self.buf.get_left() { self.buf.left(); i += 1; } // skip it while let Some(x) = self.buf.get_left() && !x.is_ascii_whitespace() { self.buf.left(); i += 1; } let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Left, i, &mut stdout)?; stdout.flush() } pub fn cursor_right_word(&mut self) -> io::Result<()> { let mut i = 0; // find word while let Some(b' ') = self.buf.get_right() { self.buf.right(); i += 1; } // skip it while let Some(x) = self.buf.get_right() && !x.is_ascii_whitespace() { self.buf.right(); i += 1; } let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Right, i, &mut stdout)?; stdout.flush() } } macro_rules! mutating_impls { ($(pub fn $fun:ident(&mut $self:ident $($arg:tt)*) -> $ty:ty $body:block )*) => { impl Line {$( pub fn $fun(&mut $self $($arg)*) -> $ty { $self.dirty = true; $body } )*} }; } mutating_impls! { pub fn clear_prompt(&mut self) -> io::Result<()> { let mut stdout = io::stdout().lock(); cursor::fmove_cursor( Direction::Right, self.buf.distance_from_right_end(), &mut stdout, )?; for _ in 0..self.buf.len() { stdout.write_all(b"\x08 \x08")?; } self.buf.clear(); stdout.flush() } pub fn type_bytes(&mut self, bs: &[u8]) -> io::Result<()> { for b in bs.iter() { self.buf.add(*b); } io::stdout().lock().write_all(&bs)?; self.buf.display_post(b""); Ok(()) } pub fn type_byte(&mut self, b: u8) -> io::Result<()> { self.type_bytes(&[b]) } pub fn del_left(&mut self) -> io::Result<()> { if self.buf.del_left().is_some() { cursor::fmove_cursor(Direction::Left, 1, &mut io::stdout())?; self.buf.display_post(b" "); } Ok(()) } pub fn del_right(&mut self) -> io::Result<()> { self.buf.del_right(); self.buf.display_post(b" "); Ok(()) } pub fn del_left_word(&mut self) -> io::Result<()> { let mut del = 0; while let Some(x) = self.buf.get_left() && x == b' ' { self.buf.del_left(); del += 1; } while let Some(x) = self.buf.get_left() && x != b' ' { self.buf.del_left(); del += 1; } cursor::fmove_cursor(Direction::Left, del, &mut io::stdout())?; self.buf.display_post(&vec![b' '; del]); Ok(()) } pub fn del_right_word(&mut self) -> io::Result<()> { let mut del = 0; while let Some(x) = self.buf.get_right() && x == b' ' { self.buf.del_right(); del += 1; } while let Some(x) = self.buf.get_right() && x != b' ' { self.buf.del_right(); del += 1; } self.buf.display_post(&vec![b' '; del]); Ok(()) } pub fn set_content(&mut self, content: Vec) -> io::Result<()> { self.buf.set_content(content); Ok(()) } pub fn dump(&mut self) -> Vec { self.buf.dump() } }