use std::str::FromStr; use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, prelude::BlockExt, style::Stylize, text::ToSpan, widgets::{Block, Widget}, }; pub mod buffer; pub mod cursor; use buffer::Buffer; use self::cursor::CursorMove; #[derive(Debug)] pub struct LuaEditor<'a> { block: Option>, scroll: usize, buffer: Buffer, } impl<'a> LuaEditor<'a> { pub fn new(content: S) -> Self where S: AsRef, { Self { block: None, scroll: 0, buffer: Buffer::from_str(content.as_ref()).unwrap(), } } pub fn block(mut self, block: Option>) -> Self { self.block = block; self } pub fn handle_key_event(&mut self, event: KeyEvent) { match event.code { KeyCode::Char(c) => self.buffer.insert(c), KeyCode::Backspace => { self.buffer.delete(); } KeyCode::Enter => { self.buffer.insert('\n'); } KeyCode::Left => self.buffer.move_cursor(CursorMove::Left(1)), KeyCode::Right => self.buffer.move_cursor(CursorMove::Right(1)), KeyCode::Up => self.buffer.move_cursor(CursorMove::Up(1)), KeyCode::Down => self.buffer.move_cursor(CursorMove::Down(1)), KeyCode::Home => {} KeyCode::End => {} KeyCode::PageUp => {} KeyCode::PageDown => {} KeyCode::Tab => self.buffer.insert('\t'), KeyCode::BackTab => {} KeyCode::Delete => {} KeyCode::Insert => {} KeyCode::F(_) => {} KeyCode::Null => {} KeyCode::Esc => {} KeyCode::CapsLock => {} KeyCode::ScrollLock => {} KeyCode::NumLock => {} KeyCode::PrintScreen => {} KeyCode::Pause => {} KeyCode::Menu => {} KeyCode::KeypadBegin => {} KeyCode::Media(_) => {} KeyCode::Modifier(_) => {} } } pub fn text(&self) -> String { self.buffer.lines().join("\n") } } impl Widget for &mut LuaEditor<'_> { fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) where Self: Sized, { self.block.render(area, buf); let inner_area = self.block.inner_if_some(area); for (i, line) in self.buffer.lines().iter().enumerate().skip(self.scroll) { let replace = &line.replace('\t', " "); let span = replace.to_span(); let mut span_area = inner_area.clone(); span_area.height = 1; span_area.y += i as u16; if !inner_area.contains(span_area.into()) { break; } span.render(span_area, buf) } let mut cursor_area = inner_area.clone(); cursor_area.width = 1; cursor_area.height = 1; cursor_area.y += self.buffer.cursor().y() as u16; cursor_area.x += self.buffer.cursor().x() as u16; let (first, _) = self .buffer .current_line() .split_at(self.buffer.cursor().x() as usize); for c in first.chars() { if c == '\t' { cursor_area.x += 1; } } self.buffer .current_line() .chars() .nth(self.buffer.cursor().x() as usize) .map(|c| if c == '\t' { ' ' } else { c }) .unwrap_or(' ') .to_span() .reversed() .render(cursor_area, buf); } }