mod buf; use std::io::{self, Write}; use crate::{ cursor::{self, CursorPos, Direction}, syntax_highlighting::Highlighter, }; pub use buf::LineBuf; pub struct Line { buf: LineBuf, dirty: bool, line_start: CursorPos, } 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, line_start: CursorPos::default(), } } pub fn set_begin_of_line(&mut self, pos: CursorPos) { self.line_start = pos; } pub fn highlight_syntax(&mut self, h: &mut Highlighter) -> std::io::Result<()> { if !self.dirty { return Ok(()); } self.dirty = false; let buf = self.into_bytes(); let highlights = if h.enabled { use crate::parse::{self, Parse}; let mut parser = parse::Cursor::new(&buf, parse::ParseMode::Completion); let _ = parse::Ast::parse(&mut parser); parser.highlights } else { Vec::new() }; let mut stdout = io::stdout().lock(); self.line_start.f_go_to(&mut stdout)?; h.pretty_print(&buf, self.left_len(), highlights, &mut stdout)?; stdout.flush()?; Ok(()) } 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() } } impl Default for Line { fn default() -> Self { Self::new() } } 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 { let result = $body; $self.dirty = true; result } )*} }; } mutating_impls! { pub fn clear_prompt(&mut self) -> io::Result<()> { self.buf.clear(); Ok(()) } pub fn type_bytes(&mut self, bs: &[u8]) -> io::Result<()> { for b in bs.iter() { self.buf.add(*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() { let mut stdout = io::stdout().lock(); cursor::fmove_cursor(Direction::Left, 1, &mut stdout)?; stdout.flush()?; } Ok(()) } pub fn del_right(&mut self) -> io::Result<()> { self.buf.del_right(); Ok(()) } pub fn del_left_word(&mut self) -> io::Result<()> { while let Some(x) = self.buf.get_left() && x == b' ' { self.buf.del_left(); } while let Some(x) = self.buf.get_left() && x != b' ' { self.buf.del_left(); } Ok(()) } pub fn del_right_word(&mut self) -> io::Result<()> { while let Some(x) = self.buf.get_right() && x == b' ' { self.buf.del_right(); } while let Some(x) = self.buf.get_right() && x != b' ' { self.buf.del_right(); } 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() } }