diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-02 20:16:54 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-02 20:16:54 +0200 |
| commit | 595bcac243cb9cdd87e7484ab102c86f3235db8a (patch) | |
| tree | c89c275b175933efc59e9cba68aff607d489a8e2 | |
| parent | de9ad07b2a4737713f1473641fe195d7e3023928 (diff) | |
add editor theme
| -rw-r--r-- | src/config/theme/editor/mod.rs | 74 | ||||
| -rw-r--r-- | src/config/theme/mod.rs | 9 | ||||
| -rw-r--r-- | src/widgets/luaeditor/mod.rs | 57 | ||||
| -rw-r--r-- | src/widgets/luaeditor/theme.rs | 160 | ||||
| -rw-r--r-- | src/widgets/luaeditor/treesitter.rs | 36 |
5 files changed, 257 insertions, 79 deletions
diff --git a/src/config/theme/editor/mod.rs b/src/config/theme/editor/mod.rs new file mode 100644 index 0000000..d9e2b16 --- /dev/null +++ b/src/config/theme/editor/mod.rs @@ -0,0 +1,74 @@ +use mlua::UserData; + +use crate::lua::evalsto::EvalTo; +use crate::widgets::luaeditor::theme::HighlightTheme; + +use super::super::GlobalConfig; +use super::style::Style; + +#[derive(Clone, Debug, Default)] +pub struct EditorTheme { + pub background: EvalTo<Style, ()>, + pub highlight: HighlightTheme, + pub cursor_line: EvalTo<Style, ()>, + pub line_number: EvalTo<Style, ()>, + pub active_line_number: EvalTo<Style, ()>, +} + +impl EditorTheme { + pub const fn new() -> Self { + Self { + background: EvalTo::Value(Style::new()), + highlight: HighlightTheme::new(), + cursor_line: EvalTo::Value(Style::new()), + line_number: EvalTo::Value(Style::new()), + active_line_number: EvalTo::Value(Style::new()), + } + } +} + +macro_rules! cfg { + () => { + GlobalConfig::instance().theme.editor + }; +} + +macro_rules! cfg_mut { + () => { + GlobalConfig::instance_mut().theme.editor + }; +} + +impl UserData for EditorTheme { + fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_function_get("background", |_, _| Ok(cfg!().background.clone())); + fields.add_field_function_set("background", |_, _, background: EvalTo<Style, ()>| { + cfg_mut!().background = background; + Ok(()) + }); + + fields.add_field_function_get("highlight", |_, _| Ok(cfg!().highlight.clone())); + fields.add_field_function_set("highlight", |_, _, highlight: HighlightTheme| { + cfg_mut!().highlight = highlight; + Ok(()) + }); + + fields.add_field_function_get("cursor_line", |_, _| Ok(cfg!().cursor_line.clone())); + fields.add_field_function_set("cursor_line", |_, _, cursor_line: EvalTo<Style, ()>| { + cfg_mut!().cursor_line = cursor_line; + Ok(()) + }); + + fields.add_field_function_get("line_number", |_, _| Ok(cfg!().line_number.clone())); + fields.add_field_function_set("line_number", |_, _, line_number: EvalTo<Style, ()>| { + cfg_mut!().line_number = line_number; + Ok(()) + }); + + fields.add_field_function_get("active_line_number", |_, _| Ok(cfg!().active_line_number.clone())); + fields.add_field_function_set("active_line_number", |_, _, active_line_number: EvalTo<Style, ()>| { + cfg_mut!().active_line_number = active_line_number; + Ok(()) + }); + } +} diff --git a/src/config/theme/mod.rs b/src/config/theme/mod.rs index 074a063..ca9723b 100644 --- a/src/config/theme/mod.rs +++ b/src/config/theme/mod.rs @@ -1,23 +1,26 @@ use mlua::UserData; -use self::sheetview::SheetViewTheme; +use self::{editor::EditorTheme, sheetview::SheetViewTheme}; use super::DUMMY_CONFIG; pub mod style; pub mod sheetview; +pub mod editor; mod bar; #[derive(Clone, Debug, Default)] pub struct Theme { pub sheetview: SheetViewTheme, + pub editor: EditorTheme, } impl Theme { pub const fn new() -> Self { Self { sheetview: SheetViewTheme::new(), + editor: EditorTheme::new(), } } } @@ -26,6 +29,10 @@ impl UserData for Theme { fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_function_get("sheetview", |_, _| { Ok(DUMMY_CONFIG.theme.sheetview) + }); + + fields.add_field_function_get("editor", |_, _| { + Ok(DUMMY_CONFIG.theme.editor) }) } } diff --git a/src/widgets/luaeditor/mod.rs b/src/widgets/luaeditor/mod.rs index 0bcffc8..c777190 100644 --- a/src/widgets/luaeditor/mod.rs +++ b/src/widgets/luaeditor/mod.rs @@ -1,9 +1,16 @@ use ratatui::{ - crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, text::{ToLine, ToSpan}, widgets::Widget + crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, + layout::Rect, + text::{ToLine, ToSpan}, + widgets::{Paragraph, Widget}, }; use tree_sitter_highlight::HighlightConfiguration; -use crate::{state::{editor::EditorState, window::Window, GlobalState}, tuicursor::TuiCursor}; +use crate::{ + config::GlobalConfig, + state::{editor::EditorState, window::Window, GlobalState}, + tuicursor::TuiCursor, +}; use crate::{cursor::CursorMove, lua}; use super::statusbar::StatusBar; @@ -87,7 +94,10 @@ impl LuaEditor { let nr_width = (buffer.lines().len().to_string().len() + 1).max(4) as u16; Some(TuiCursor { - position: (buffer.cursor().y() as u16, buffer.cursor().x() as u16 + nr_width), + position: ( + buffer.cursor().y() as u16, + buffer.cursor().x() as u16 + nr_width, + ), style: ratatui::crossterm::cursor::SetCursorStyle::SteadyBar, }) } @@ -101,6 +111,9 @@ impl Widget for &mut LuaEditor { let state = GlobalState::instance(); let buffer = &state.editor.buffer; + let config = GlobalConfig::instance(); + let theme = &config.theme.editor; + self.bar.render(area, buf); let inner_area = self.bar.area(area); @@ -114,6 +127,28 @@ impl Widget for &mut LuaEditor { let mut span_area = text_area; let mut current_line = 0; + theme + .background + .get((), &lua::get()) + .unwrap_or_default() + .apply(Paragraph::default()) + .render(area, buf); + + theme + .cursor_line + .get((), &lua::get()) + .unwrap_or_default() + .apply(Paragraph::default()) + .render( + Rect::new( + text_area.x, + text_area.y + buffer.cursor().y() as u16, + text_area.width, + 1, + ), + buf, + ); + for (hl, group) in highlights.iter() { if *group == "\n" { if current_line >= self.scroll { @@ -127,7 +162,7 @@ impl Widget for &mut LuaEditor { let span = group.to_span(); if inner_area.contains(span_area.into()) { - theme::theme_highlight_group(*hl, span).render(span_area, buf); + theme.highlight.highlight(*hl, span).render(span_area, buf); } span_area.x += group.len() as u16; @@ -137,7 +172,7 @@ impl Widget for &mut LuaEditor { 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.width = nr_width; nr_area.y = inner_area.y + (i - self.scroll) as u16; nr_area.height = 1; @@ -145,7 +180,17 @@ impl Widget for &mut LuaEditor { break; } - (i + 1).to_line().right_aligned().render(nr_area, buf); + if buffer.cursor().y() == i { + theme + .active_line_number + .get((), &lua::get()) + .unwrap_or_default() + } else { + theme.line_number.get((), &lua::get()).unwrap_or_default() + } + .apply(((i + 1).to_string() + " ").to_line()) + .right_aligned() + .render(nr_area, buf); } } } diff --git a/src/widgets/luaeditor/theme.rs b/src/widgets/luaeditor/theme.rs index 38e4498..f0520f4 100644 --- a/src/widgets/luaeditor/theme.rs +++ b/src/widgets/luaeditor/theme.rs @@ -1,45 +1,127 @@ -use ratatui::{ - style::{Color, Stylize}, - text::Span, -}; +use mlua::{FromLua, IntoLua}; +use ratatui::{style::Color, text::Span}; use tree_sitter_highlight::Highlight; -static HIGHLIGHT_THEME: [Color; 29] = [ - Color::White, // attribute - Color::White, // boolean - Color::DarkGray, // comment - Color::LightYellow, // conditional - Color::White, // constant - Color::White, // constant.builtin - Color::White, // constructor - Color::White, // field - Color::White, // function - Color::White, // function.builtin - Color::White, // function.call - Color::Yellow, // keyword - Color::LightYellow, // keyword.function - Color::White, // keyword.operator - Color::LightYellow, // keyword.return - Color::White, // label - Color::White, // method - Color::White, // method.call - Color::Blue, // number - Color::Gray, // operator - Color::White, // parameter - Color::White, // preproc - Color::Gray, // punctuation.bracket - Color::Gray, // punctuation.delimiter - Color::LightYellow, // repeat - Color::LightGreen, // string - Color::Yellow, // string.escape - Color::Gray, // variable - Color::White, // variable.builtin +use crate::config::theme::style::Style; + +pub(super) static HIGHLIGHT_NAMES: [&str; 29] = [ + "attribute", + "boolean", + "comment", + "conditional", + "constant", + "constant.builtin", + "constructor", + "field", + "function", + "function.builtin", + "function.call", + "keyword", + "keyword.function", + "keyword.operator", + "keyword.return", + "label", + "method", + "method.call", + "number", + "operator", + "parameter", + "preproc", + "punctuation.bracket", + "punctuation.delimiter", + "repeat", + "string", + "string.escape", + "variable", + "variable.builtin", ]; -pub fn theme_highlight_group(hl: Option<Highlight>, span: Span) -> Span { - if let Some(hl) = hl { - span.fg(HIGHLIGHT_THEME[hl.0]) - } else { - span +#[derive(Default, Debug, Clone)] +pub struct HighlightTheme { + theme: [Style; 29], +} + +impl HighlightTheme { + pub const fn new() -> Self { + Self { + theme: [ + Style::new().fg(Color::White), // attribute + Style::new().fg(Color::White), // boolean + Style::new().fg(Color::White), // comment + Style::new().fg(Color::White), // conditional + Style::new().fg(Color::White), // constant + Style::new().fg(Color::White), // constant.builtin + Style::new().fg(Color::White), // constructor + Style::new().fg(Color::White), // field + Style::new().fg(Color::White), // function + Style::new().fg(Color::White), // function.builtin + Style::new().fg(Color::White), // function.call + Style::new().fg(Color::White), // keyword + Style::new().fg(Color::White), // keyword.function + Style::new().fg(Color::White), // keyword.operator + Style::new().fg(Color::White), // keyword.return + Style::new().fg(Color::White), // label + Style::new().fg(Color::White), // method + Style::new().fg(Color::White), // method.call + Style::new().fg(Color::White), // number + Style::new().fg(Color::White), // operator + Style::new().fg(Color::White), // parameter + Style::new().fg(Color::White), // preproc + Style::new().fg(Color::White), // punctuation.bracket + Style::new().fg(Color::White), // punctuation.delimiter + Style::new().fg(Color::White), // repeat + Style::new().fg(Color::White), // string + Style::new().fg(Color::White), // string.escape + Style::new().fg(Color::White), // variable + Style::new().fg(Color::White), // variable.builtin + ], + } + } + + pub fn highlight<'a>(&'a self, hl: Option<Highlight>, span: Span<'a>) -> Span<'a> { + if let Some(hl) = hl { + self.theme[hl.0].apply(span) + } else { + span + } + } +} + +impl<'lua> IntoLua<'lua> for HighlightTheme { + fn into_lua( + self, + lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> { + let table = lua.create_table()?; + + for (i, names) in HIGHLIGHT_NAMES.iter().enumerate() { + table.set(names.replace('.', "_"), self.theme[i])? + } + + table.into_lua(lua) + } +} + +impl<'lua> FromLua<'lua> for HighlightTheme { + fn from_lua( + value: mlua::prelude::LuaValue<'lua>, + _lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<Self> { + let mut theme = HighlightTheme::default(); + + if value.is_table() { + let table = value.as_table().unwrap(); + for (i, names) in HIGHLIGHT_NAMES.iter().enumerate() { + if let Ok(style) = table.get(names.replace('.', "_")) { + theme.theme[i] = style; + } + } + + Ok(theme) + } else { + Err(mlua::prelude::LuaError::runtime( + "failed to parse HighlightTheme", + )) + } } } diff --git a/src/widgets/luaeditor/treesitter.rs b/src/widgets/luaeditor/treesitter.rs index 1474434..ecd782d 100644 --- a/src/widgets/luaeditor/treesitter.rs +++ b/src/widgets/luaeditor/treesitter.rs @@ -1,36 +1,6 @@ use tree_sitter_highlight::{Highlight, HighlightConfiguration, HighlightEvent, Highlighter}; -static HIGHLIGHT_NAMES: [&str; 29] = [ - "attribute", - "boolean", - "comment", - "conditional", - "constant", - "constant.builtin", - "constructor", - "field", - "function", - "function.builtin", - "function.call", - "keyword", - "keyword.function", - "keyword.operator", - "keyword.return", - "label", - "method", - "method.call", - "number", - "operator", - "parameter", - "preproc", - "punctuation.bracket", - "punctuation.delimiter", - "repeat", - "string", - "string.escape", - "variable", - "variable.builtin", -]; +use super::theme; pub fn new_highlight_configuration() -> HighlightConfiguration { let mut highlight_config = HighlightConfiguration::new( @@ -42,13 +12,13 @@ pub fn new_highlight_configuration() -> HighlightConfiguration { ) .unwrap(); - highlight_config.configure(&HIGHLIGHT_NAMES); + highlight_config.configure(&theme::HIGHLIGHT_NAMES); highlight_config } pub fn highlight_name(h: Highlight) -> &'static str { - HIGHLIGHT_NAMES[h.0] + theme::HIGHLIGHT_NAMES[h.0] } pub fn highlighter_split<'a>( |