use ratatui::{ crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, style::{Style, Stylize}, text::{ToLine, ToSpan}, widgets::Widget, }; use tree_sitter_highlight::HighlightConfiguration; use crate::state::{editor::EditorState, window::Window, GlobalState}; use crate::{cursor::CursorMove, lua}; use super::statusbar::StatusBar; pub mod theme; pub mod treesitter; pub struct LuaEditor { bar: StatusBar, scroll: usize, highlight_config: HighlightConfiguration, } impl LuaEditor { pub fn new() -> Self { Self { bar: StatusBar::new() .left(" [No Name] ") .left_style(Style::default().on_magenta()), scroll: 0, highlight_config: treesitter::new_highlight_configuration(), } } pub fn handle_key_event(&mut self, event: KeyEvent) { match event.code { KeyCode::Char('r') if event.modifiers == KeyModifiers::CONTROL => { EditorState::run(&lua::get()) } KeyCode::Char(c) => GlobalState::instance_mut().editor.buffer.insert(c), KeyCode::Backspace => { GlobalState::instance_mut().editor.buffer.delete(); } KeyCode::Enter => { GlobalState::instance_mut().editor.buffer.insert('\n'); } KeyCode::Left => GlobalState::instance_mut() .editor .buffer .move_cursor(CursorMove::Left(1)), KeyCode::Right => GlobalState::instance_mut() .editor .buffer .move_cursor(CursorMove::Right(1)), KeyCode::Up => GlobalState::instance_mut() .editor .buffer .move_cursor(CursorMove::Up(1)), KeyCode::Down => GlobalState::instance_mut() .editor .buffer .move_cursor(CursorMove::Down(1)), KeyCode::Home => {} KeyCode::End => {} KeyCode::PageUp => {} KeyCode::PageDown => {} KeyCode::Tab => GlobalState::instance_mut().editor.buffer.insert('\t'), KeyCode::BackTab => {} KeyCode::Delete => {} KeyCode::Insert => {} KeyCode::F(_) => {} KeyCode::Null => {} KeyCode::Esc => { let mut state = GlobalState::instance_mut(); state.editor.visible = false; state.set_focus(Window::View) } KeyCode::CapsLock => {} KeyCode::ScrollLock => {} KeyCode::NumLock => {} KeyCode::PrintScreen => {} KeyCode::Pause => {} KeyCode::Menu => {} KeyCode::KeypadBegin => {} KeyCode::Media(_) => {} KeyCode::Modifier(_) => {} } } } impl Widget for &mut LuaEditor { fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) where Self: Sized, { let state = GlobalState::instance(); let buffer = &state.editor.buffer; self.bar.render(area, buf); let inner_area = self.bar.area(area); let text = buffer.as_string(); let highlights = treesitter::highlighter_split(text.as_bytes(), &self.highlight_config); let nr_width = (buffer.lines().len().to_string().len() + 1).max(4) as u16; let mut text_area = inner_area; text_area.x += nr_width; let mut span_area = text_area; let mut current_line = 0; for (hl, group) in highlights.iter() { if *group == "\n" { if current_line >= self.scroll { span_area.y += 1; span_area.x = text_area.x; } current_line += 1; } else if current_line >= self.scroll { let group = group.replace('\t', " "); let span = group.to_span(); if inner_area.contains(span_area.into()) { theme::theme_highlight_group(*hl, span).render(span_area, buf); } span_area.x += group.len() as u16; } } for i in self.scroll..buffer.lines().len() { let mut nr_area = span_area; nr_area.x = inner_area.x; nr_area.width = nr_width - 1; nr_area.y = inner_area.y + (i - self.scroll) as u16; nr_area.height = 1; if !inner_area.contains(nr_area.into()) { break; } (i + 1).to_line().right_aligned().render(nr_area, buf); } let mut cursor_area = text_area; cursor_area.width = 1; cursor_area.height = 1; cursor_area.y += buffer.cursor().y() as u16; cursor_area.x += buffer.cursor().x() as u16; let (first, _) = buffer.current_line().split_at(buffer.cursor().x()); for c in first.chars() { if c == '\t' { cursor_area.x += 1; } } if self.scroll > buffer.cursor().y() { self.scroll = buffer.cursor().y(); } else if inner_area.height as usize + self.scroll - 1 < buffer.cursor().y() { self.scroll = buffer.cursor().y() - inner_area.height as usize + 1; } cursor_area.y -= self.scroll as u16; if inner_area.contains(cursor_area.into()) { buffer .current_line() .chars() .nth(buffer.cursor().x()) .map(|c| if c == '\t' { ' ' } else { c }) .unwrap_or(' ') .to_span() .reversed() .render(cursor_area, buf); } } } impl Default for LuaEditor { fn default() -> Self { Self { highlight_config: treesitter::new_highlight_configuration(), bar: StatusBar::default(), scroll: 0, } } }