diff options
Diffstat (limited to 'src/widgets/luaeditor')
| -rw-r--r-- | src/widgets/luaeditor/buffer.rs | 24 | ||||
| -rw-r--r-- | src/widgets/luaeditor/mod.rs | 62 | ||||
| -rw-r--r-- | src/widgets/luaeditor/theme.rs | 43 | ||||
| -rw-r--r-- | src/widgets/luaeditor/treesitter.rs | 75 |
4 files changed, 178 insertions, 26 deletions
diff --git a/src/widgets/luaeditor/buffer.rs b/src/widgets/luaeditor/buffer.rs index 6a954ed..905950e 100644 --- a/src/widgets/luaeditor/buffer.rs +++ b/src/widgets/luaeditor/buffer.rs @@ -32,16 +32,22 @@ impl Buffer { fn end_balance(&self, level: usize) -> isize { let str = "\t".repeat(level); - let start_count = self.lines.iter().filter(|s| { - s.starts_with(&(str.clone() + "if ")) - || s.starts_with(&(str.clone() + "for ")) - || s.starts_with(&(str.clone() + "while ")) - || (s.starts_with(&str) && s.contains("function(") && s.ends_with(")")) - }).count(); + let start_count = self + .lines + .iter() + .filter(|s| { + s.starts_with(&(str.clone() + "if ")) + || s.starts_with(&(str.clone() + "for ")) + || s.starts_with(&(str.clone() + "while ")) + || (s.starts_with(&str) && s.contains("function(") && s.ends_with(")")) + }) + .count(); - let end_count = self.lines.iter().filter( - |s| s.starts_with(&(str.clone() + "end ")) || **s == (str.clone() + "end") - ).count(); + let end_count = self + .lines + .iter() + .filter(|s| s.starts_with(&(str.clone() + "end ")) || **s == (str.clone() + "end")) + .count(); (end_count as isize) - (start_count as isize) } diff --git a/src/widgets/luaeditor/mod.rs b/src/widgets/luaeditor/mod.rs index 723d8b5..9a73e6e 100644 --- a/src/widgets/luaeditor/mod.rs +++ b/src/widgets/luaeditor/mod.rs @@ -10,16 +10,19 @@ use ratatui::{ pub mod buffer; pub mod cursor; +pub mod theme; +pub mod treesitter; use buffer::Buffer; +use tree_sitter_highlight::HighlightConfiguration; use self::cursor::CursorMove; -#[derive(Debug)] pub struct LuaEditor<'a> { block: Option<Block<'a>>, scroll: usize, buffer: Buffer, + highlight_config: HighlightConfiguration, } impl<'a> LuaEditor<'a> { @@ -31,6 +34,7 @@ impl<'a> LuaEditor<'a> { block: None, scroll: 0, buffer: Buffer::from_str(content.as_ref()).unwrap(), + highlight_config: treesitter::new_highlight_configuration(), } } @@ -88,18 +92,40 @@ impl Widget for &mut LuaEditor<'_> { 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; + let text = self.text(); + let highlights = treesitter::highlighter_split(text.as_bytes(), &self.highlight_config); + + let mut span_area = inner_area.clone(); + + for (hl, group) in highlights.iter().skip(self.scroll) { + let lines: Vec<_> = group.lines().collect(); + + for (i, line) in lines.iter().enumerate() { + let line = line.replace("\t", " "); + let span = line.to_span(); + + if !inner_area.contains(span_area.into()) { + break; + } + + theme::theme_highlight_group(*hl, span).render(span_area, buf); + + if i < lines.len() - 1 { + span_area.y += 1; + span_area.x = inner_area.x; + } else { + span_area.x += line.len() as u16; + } + } if !inner_area.contains(span_area.into()) { break; } - span.render(span_area, buf) + if group.ends_with("\n") { + span_area.y += 1; + span_area.x = inner_area.x; + } } let mut cursor_area = inner_area.clone(); @@ -119,14 +145,16 @@ impl Widget for &mut LuaEditor<'_> { } } - 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); + if inner_area.contains(cursor_area.into()) { + 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); + } } } diff --git a/src/widgets/luaeditor/theme.rs b/src/widgets/luaeditor/theme.rs new file mode 100644 index 0000000..2af8534 --- /dev/null +++ b/src/widgets/luaeditor/theme.rs @@ -0,0 +1,43 @@ +use ratatui::{style::{Color, Stylize}, 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 +]; + +pub fn theme_highlight_group(hl: Option<Highlight>, span: Span) -> Span { + if let Some(hl) = hl { + span.fg(HIGHLIGHT_THEME[hl.0]) + } else { + span + } +} diff --git a/src/widgets/luaeditor/treesitter.rs b/src/widgets/luaeditor/treesitter.rs new file mode 100644 index 0000000..57013f9 --- /dev/null +++ b/src/widgets/luaeditor/treesitter.rs @@ -0,0 +1,75 @@ +use tree_sitter_highlight::{Highlight, HighlightConfiguration, HighlightEvent, Highlighter}; + +static HIGHLIGHT_NAMES: [&'static 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 new_highlight_configuration() -> HighlightConfiguration { + let mut highlight_config = HighlightConfiguration::new( + tree_sitter_lua::language(), + "lua", + tree_sitter_lua::HIGHLIGHTS_QUERY, + tree_sitter_lua::INJECTIONS_QUERY, + tree_sitter_lua::LOCALS_QUERY, + ) + .unwrap(); + + highlight_config.configure(&HIGHLIGHT_NAMES); + + highlight_config +} + +pub fn highlight_name(h: Highlight) -> &'static str { + HIGHLIGHT_NAMES[h.0] +} + +pub fn highlighter_split<'a>( + s: &'a [u8], + config: &'a HighlightConfiguration, +) -> Vec<(Option<Highlight>, &'a str)> { + let mut splits = Vec::new(); + let mut current = None; + + let mut highlighter = Highlighter::new(); + let highlights = highlighter.highlight(config, s, None, |_| None).unwrap(); + + for event in highlights { + match event.unwrap() { + HighlightEvent::Source { start, end } => { + splits.push((current, std::str::from_utf8(&s[start..end]).unwrap())); + } + HighlightEvent::HighlightStart(s) => current = Some(s), + HighlightEvent::HighlightEnd => current = None, + } + } + + splits +} |